From dbc1df378ccbbfacdcc517b4c5f6fc98bb42f957 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 27 Feb 2019 11:20:26 +0100 Subject: [PATCH 0001/5147] Master is now 2.4 --- ChangeLog | 7 +++++++ doc/whatsnew/2.4.rst | 18 ++++++++++++++++++ doc/whatsnew/index.rst | 1 + pylint/__pkginfo__.py | 2 +- 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 doc/whatsnew/2.4.rst diff --git a/ChangeLog b/ChangeLog index 0d6813ebdb..5db7ce3167 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,13 @@ Pylint's ChangeLog ------------------ +What's New in Pylint 2.4.0? +=========================== + +Release date: TBA + + + What's New in Pylint 2.3.0? =========================== diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst new file mode 100644 index 0000000000..c116721f2a --- /dev/null +++ b/doc/whatsnew/2.4.rst @@ -0,0 +1,18 @@ +************************** + What's New in Pylint 2.4 +************************** + +:Release: 2.4 +:Date: TBA + + +Summary -- Release highlights +============================= + + +New checkers +============ + + +Other Changes +============= diff --git a/doc/whatsnew/index.rst b/doc/whatsnew/index.rst index 6dd43c8666..e7faa7cc76 100644 --- a/doc/whatsnew/index.rst +++ b/doc/whatsnew/index.rst @@ -9,6 +9,7 @@ High level descriptions of the most important changes between major Pylint versi .. toctree:: :maxdepth: 1 + 2.4.rst 2.3.rst 2.2.rst 2.1.rst diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index 2082a2faf4..b7b487fad5 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -25,7 +25,7 @@ modname = distname = "pylint" -numversion = (2, 3, 0) +numversion = (2, 4, 0) dev_version = None string_version = ".".join(str(num) for num in numversion) From 66f36eec1e3daf86373739113f656b2e11e870b5 Mon Sep 17 00:00:00 2001 From: jab Date: Wed, 27 Feb 2019 16:29:10 -0500 Subject: [PATCH 0002/5147] Pin dependencies to non-breaking version ranges --- pylint/__pkginfo__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index b7b487fad5..fb1fc296c3 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -34,7 +34,7 @@ else: version = string_version -install_requires = ["astroid>=2.2.0", "isort >= 4.2.5", "mccabe"] +install_requires = ["astroid>=2.2.0,<3", "isort>=4.2.5,<5", "mccabe>=0.6,<0.7"] dependency_links = [] # type: ignore From 6b1adc668727ebfbd84763b14b65676cc11febec Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 28 Feb 2019 17:18:00 +0100 Subject: [PATCH 0003/5147] Pass `quote=False` to `html.escape` in the JSON reporter Close PyCQA/pylint#2769 --- pylint/reporters/json.py | 3 +-- pylint/test/regrtest_data/unused_variable.py | 6 ++++++ pylint/test/test_self.py | 22 ++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 pylint/test/regrtest_data/unused_variable.py diff --git a/pylint/reporters/json.py b/pylint/reporters/json.py index 100ed52648..18d46bd8f1 100644 --- a/pylint/reporters/json.py +++ b/pylint/reporters/json.py @@ -39,8 +39,7 @@ def handle_message(self, msg): "column": msg.column, "path": msg.path, "symbol": msg.symbol, - # pylint: disable=deprecated-method; deprecated since 3.2. - "message": html.escape(msg.msg or ""), + "message": html.escape(msg.msg or "", quote=False), "message-id": msg.msg_id, } ) diff --git a/pylint/test/regrtest_data/unused_variable.py b/pylint/test/regrtest_data/unused_variable.py new file mode 100644 index 0000000000..eee909d531 --- /dev/null +++ b/pylint/test/regrtest_data/unused_variable.py @@ -0,0 +1,6 @@ +# pylint: disable=missing-docstring + +def test(): + variable = '' + variable2 = None + return variable2 diff --git a/pylint/test/test_self.py b/pylint/test/test_self.py index 2c3907e052..d03566eebf 100644 --- a/pylint/test/test_self.py +++ b/pylint/test/test_self.py @@ -433,6 +433,28 @@ def test_json_report_when_file_is_missing(self): assert message[key] == value assert message["message"].startswith("No module named") + def test_json_report_does_not_escape_quotes(self): + out = StringIO() + module = join(HERE, "regrtest_data", "unused_variable.py") + self._runtest([module], code=4, reporter=JSONReporter(out)) + output = json.loads(out.getvalue()) + assert isinstance(output, list) + assert len(output) == 1 + assert isinstance(output[0], dict) + expected = { + "symbol": "unused-variable", + "module": "unused_variable", + "column": 4, + "message": "Unused variable 'variable'", + "message-id": "W0612", + "line": 4, + "type": "warning", + } + message = output[0] + for key, value in expected.items(): + assert key in message + assert message[key] == value + def test_information_category_disabled_by_default(self): expected = "Your code has been rated at 10.00/10" path = join(HERE, "regrtest_data", "meta.py") From 784dc5319bcad18e390f6cb7d44df8fda7514722 Mon Sep 17 00:00:00 2001 From: Dan Hemberger <846186+hemberger@users.noreply.github.com> Date: Thu, 28 Feb 2019 16:38:02 -0800 Subject: [PATCH 0004/5147] Fix trivial typo in 2.3.rst "upports" -> "supports" --- doc/whatsnew/2.3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/whatsnew/2.3.rst b/doc/whatsnew/2.3.rst index a90aec9b47..732618526d 100644 --- a/doc/whatsnew/2.3.rst +++ b/doc/whatsnew/2.3.rst @@ -41,7 +41,7 @@ For the full changes, check the Changelog. * We fixed some false positives for ``no-self-argument`` and ``unsubscriptable-object`` when using ``__class_getitem__`` (new in Python 3.7) -* ``pylint`` now upports ``Ellipsis`` as a synonym for ``pass`` statements. +* ``pylint`` now supports ``Ellipsis`` as a synonym for ``pass`` statements. * ``fixme`` gets triggered only on comments. From 694617dc510de87c96b4f8391a7ecc019b4dc39b Mon Sep 17 00:00:00 2001 From: Kylian Date: Wed, 27 Feb 2019 20:39:41 +0100 Subject: [PATCH 0005/5147] Added method arguments to the dot file writer --- CONTRIBUTORS.txt | 2 ++ ChangeLog | 2 ++ pylint/pyreverse/writer.py | 6 +++++- pylint/test/data/classes_No_Name.dot | 4 ++-- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 4a4336afd8..2277567955 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -271,3 +271,5 @@ contributors: * Federico Bond: contributor * Fantix King (UChicago): contributor + +* Goudcode: contributor \ No newline at end of file diff --git a/ChangeLog b/ChangeLog index 5db7ce3167..0582f7c5ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,7 +7,9 @@ What's New in Pylint 2.4.0? Release date: TBA +* Added method arguments to the dot writer for pyreverse. +Close #2139 What's New in Pylint 2.3.0? =========================== diff --git a/pylint/pyreverse/writer.py b/pylint/pyreverse/writer.py index 332c28838a..e541845cf8 100644 --- a/pylint/pyreverse/writer.py +++ b/pylint/pyreverse/writer.py @@ -131,7 +131,11 @@ def get_values(self, obj): if not self.config.only_classnames: label = r"%s|%s\l|" % (label, r"\l".join(obj.attrs)) for func in obj.methods: - label = r"%s%s()\l" % (label, func.name) + args = [] + for arg in func.args.args: + if arg.name != 'self': + args.append(arg.name) + label = r"%s%s(%s)\l" % (label, func.name, ', '.join(args)) label = "{%s}" % label if is_exception(obj.node): return dict(fontcolor="red", label=label, shape="record") diff --git a/pylint/test/data/classes_No_Name.dot b/pylint/test/data/classes_No_Name.dot index 3a9df79671..8867b4e416 100644 --- a/pylint/test/data/classes_No_Name.dot +++ b/pylint/test/data/classes_No_Name.dot @@ -1,9 +1,9 @@ digraph "classes_No_Name" { charset="utf-8" rankdir=BT -"0" [label="{Ancestor|attr : str\lcls_member\l|get_value()\lset_value()\l}", shape="record"]; +"0" [label="{Ancestor|attr : str\lcls_member\l|get_value()\lset_value(value)\l}", shape="record"]; "1" [label="{DoNothing|\l|}", shape="record"]; -"2" [label="{Interface|\l|get_value()\lset_value()\l}", shape="record"]; +"2" [label="{Interface|\l|get_value()\lset_value(value)\l}", shape="record"]; "3" [label="{Specialization|TYPE : str\lrelation\ltop : str\l|}", shape="record"]; "3" -> "0" [arrowhead="empty", arrowtail="none"]; "0" -> "2" [arrowhead="empty", arrowtail="node", style="dashed"]; From c892e04cf25670128064aae6fdf590183c698ed2 Mon Sep 17 00:00:00 2001 From: Kylian Date: Wed, 27 Feb 2019 23:09:21 +0100 Subject: [PATCH 0006/5147] Changed quotes to pass the format check --- pylint/pyreverse/writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylint/pyreverse/writer.py b/pylint/pyreverse/writer.py index e541845cf8..c0322bd9bc 100644 --- a/pylint/pyreverse/writer.py +++ b/pylint/pyreverse/writer.py @@ -133,9 +133,9 @@ def get_values(self, obj): for func in obj.methods: args = [] for arg in func.args.args: - if arg.name != 'self': + if arg.name != "self": args.append(arg.name) - label = r"%s%s(%s)\l" % (label, func.name, ', '.join(args)) + label = r"%s%s(%s)\l" % (label, func.name, ", ".join(args)) label = "{%s}" % label if is_exception(obj.node): return dict(fontcolor="red", label=label, shape="record") From 946657c399d13ca30ed0489099c964b0163e98a4 Mon Sep 17 00:00:00 2001 From: Kylian Date: Thu, 28 Feb 2019 17:09:23 +0100 Subject: [PATCH 0007/5147] Replaced args loop with comprehension --- pylint/pyreverse/writer.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pylint/pyreverse/writer.py b/pylint/pyreverse/writer.py index c0322bd9bc..c97ba9bf5e 100644 --- a/pylint/pyreverse/writer.py +++ b/pylint/pyreverse/writer.py @@ -131,10 +131,7 @@ def get_values(self, obj): if not self.config.only_classnames: label = r"%s|%s\l|" % (label, r"\l".join(obj.attrs)) for func in obj.methods: - args = [] - for arg in func.args.args: - if arg.name != "self": - args.append(arg.name) + args = [arg.name for arg in func.args.args if arg.name != "self"] label = r"%s%s(%s)\l" % (label, func.name, ", ".join(args)) label = "{%s}" % label if is_exception(obj.node): From 6fe3cbc799ff5ebb282431c22238d1d37b656cdb Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sat, 2 Mar 2019 14:22:43 +0100 Subject: [PATCH 0008/5147] Try automatic deployments for each release tag --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8730c87059..c7b2b08894 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,3 +59,12 @@ notifications: email: on_success: always on_failure: always +deploy: + provider: pypi + user: Claudiu.Popa + password: + secure: "EEuu9Rn6yhZjHaRPPHorFOWHu13xmMan+lqXh1jmusbEv9weRSz1LH38ziO7+HIST9l8Sup5tfKr9e0nzYw128FaTRf9/5lMJRa3ZLzh+152EfUqOUfcKKDj7rMN9qeWnS6NjKlUubQypUPkgGfFH4c0jHNVU9qUCLFXLkrFsUUcDrU86nPu69hLOfmyMmv1SF/BaOI1SagKbs5Ak1v0ZGHqImzwqs1Aj6Bz7WzXm70jGuVqokCq6HQNHD3aGjgyQTkB9J5cevNY0br7RL7UZMQMPXYFIsXCKmZ1CyEibfiM/TQBry5vVdBTnrSGyFa3/tke96P64ASRu8/V5icx8jPrrZ1zzmNuW9uMRhD1gNhtLNma/g5qbwAnAAaxMON4EpARzQ1ZYJnLVhohKhlKtZyp99Rv0KlLgoOvlFQH0rwTE9kJaiaBApWB5ITREP68XTjQH8OezjiwkmyMNLhG7SUFG8vGWN90oTfBX7thb+reiAUsH5nuNGCx/gzQ8lxdHt/9GrgJCilopjJbdPTF/twcS6tSXrcqAhjktlHMPe3FohnkWbgoKeDznfm/yw3Ux3mYZrrde/Tl+B/InQsTFk9sTMafpEitt9qyA8x5PBNxofXBqLtGlsWfh9O0bHoMVlVd1Nm44WSMnpgFE0QY/dtOsBq1cpuxut2xWwJSr6I=" + on: + tags: true + condition: "$TOXENV = py36" + distributions: sdist bdist_wheel From 01b5dd649f2f84f6e3d5907065db0d7676948bd6 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sat, 2 Mar 2019 14:26:04 +0100 Subject: [PATCH 0009/5147] Update the release process following the Travis autodeployments --- doc/release.txt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/doc/release.txt b/doc/release.txt index 2754324cb7..3b0fffce58 100644 --- a/doc/release.txt +++ b/doc/release.txt @@ -12,8 +12,9 @@ Pre-developing 2. create a new ``What's new in Pylint X.Y`` document. Take a look at the examples from doc/whatsnew. -Pre-release ------------ + +Release process +--------------- 1. Preparation 0. Run the acceptance tests to see if everything is alright with this release. @@ -33,14 +34,15 @@ Pre-release 2. Make sure the tests are passing on Travis: https://travis-ci.org/PyCQA/pylint/ -3. Add a new tag 'pylint-$VERSION' +3. Do the actual release by tagging the master with 'pylint-$VERSION'. Travis should deal + with the release process once the tag is pushed with `git push origin --tags` -4. Publish all remaining changes to the GitHub repository: - https://github.com/PyCQA/pylint. - Also push the tag with `git push origin --tags` +Manual Release +-------------- -Release by running the following: +Following the previous steps, for a manual release run the following commands: $ git clean -fdx && find . -name '*.pyc' -delete $ python setup.py sdist --formats=gztar bdist_wheel $ twine upload dist/* + $ # don't forget to tag it as well From fb5a2a6dd70f4f8dae84c92c10d88dbe29c20dd6 Mon Sep 17 00:00:00 2001 From: Thomas Hisch Date: Sat, 2 Mar 2019 14:28:31 +0100 Subject: [PATCH 0010/5147] Add support for reading from stdin (#2746) pylint gained a new `--from-stdin` flag which activates stdin linting, useful for editors and similar use cases. Closes: #1187 --- CONTRIBUTORS.txt | 7 ++- ChangeLog | 5 ++ doc/whatsnew/2.3.rst | 5 ++ pylint/exceptions.py | 4 ++ pylint/lint.py | 101 ++++++++++++++++++++++++++++++--------- pylint/test/test_self.py | 77 +++++++++++++++++++++++++++++ 6 files changed, 176 insertions(+), 23 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 2277567955..e01a3d2cf7 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -272,4 +272,9 @@ contributors: * Fantix King (UChicago): contributor -* Goudcode: contributor \ No newline at end of file +* Thomas Hisch: contributor + +* Clément Pit-Claudel : contributor + +* Goudcode: contributor + diff --git a/ChangeLog b/ChangeLog index 0582f7c5ef..26eda11569 100644 --- a/ChangeLog +++ b/ChangeLog @@ -148,6 +148,11 @@ Release date: 2019-02-27 * Fix false positive ``useless-else-on-loop`` if the break is deep in the else of an inner loop. +* Support for linting file from stdin. + + IDEs may benefit from the support for linting from an in-memory file. + + Close #1187 What's New in Pylint 2.2.2? =========================== diff --git a/doc/whatsnew/2.3.rst b/doc/whatsnew/2.3.rst index 732618526d..4f4fa436b3 100644 --- a/doc/whatsnew/2.3.rst +++ b/doc/whatsnew/2.3.rst @@ -65,3 +65,8 @@ For the full changes, check the Changelog. * There's a new command line option ``list-groups`` for listing all the check groups ``pylint`` knows about. This is useful to know what groups you can disable or enable individually. + +* A file is now read from stdin if the ``--from-stdin`` flag is used on the + command line. In addition to the ``--from-stdin`` flag a (single) file + name needs to be specified on the command line, which is needed for the + report. diff --git a/pylint/exceptions.py b/pylint/exceptions.py index 57353a4669..d5dd17fb20 100644 --- a/pylint/exceptions.py +++ b/pylint/exceptions.py @@ -23,3 +23,7 @@ class EmptyReportError(Exception): class InvalidReporterError(Exception): """raised when selected reporter is invalid (e.g. not found)""" + + +class InvalidArgsError(ValueError): + """raised when passed arguments are invalid, e.g., have the wrong length""" diff --git a/pylint/lint.py b/pylint/lint.py index a0c970a560..1b53b16589 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -62,6 +62,7 @@ import collections import contextlib +from io import TextIOWrapper import operator import os @@ -75,6 +76,7 @@ import astroid from astroid.__pkginfo__ import version as astroid_version +from astroid.builder import AstroidBuilder from astroid import modutils from pylint import checkers from pylint import interfaces @@ -89,6 +91,21 @@ MANAGER = astroid.MANAGER +def _ast_from_string(data, filepath, modname): + cached = MANAGER.astroid_cache.get(modname) + if cached and cached.file == filepath: + return cached + + return AstroidBuilder(MANAGER).string_build(data, modname, filepath) + + +def _read_stdin(): + # https://mail.python.org/pipermail/python-list/2012-November/634424.html + # FIXME should this try to check the file's declared encoding? + sys.stdin = TextIOWrapper(sys.stdin.detach(), encoding="utf-8") + return sys.stdin.read() + + def _get_new_args(message): location = ( message.abspath, @@ -556,6 +573,16 @@ def make_options(): ), }, ), + ( + "from-stdin", + { + "action": "store_true", + "help": ( + "Interpret the stdin as a python script, whose filename " + "needs to be passed as the module_or_package argument." + ), + }, + ), ) option_groups = ( @@ -1054,31 +1081,61 @@ def _do_check(self, files_or_modules): if interfaces.implements(checker, interfaces.IAstroidChecker): walker.add_checker(checker) # build ast and check modules or packages - for descr in self.expand_files(files_or_modules): - modname, filepath, is_arg = descr["name"], descr["path"], descr["isarg"] - if not self.should_analyze_file(modname, filepath, is_argument=is_arg): - continue + if self.config.from_stdin: + if len(files_or_modules) != 1: + raise exceptions.InvalidArgsError( + "Missing filename required for --from-stdin" + ) + + filepath = files_or_modules[0] + try: + # Note that this function does not really perform an + # __import__ but may raise an ImportError exception, which + # we want to catch here. + modname = ".".join(modutils.modpath_from_file(filepath)) + except ImportError: + modname = os.path.splitext(os.path.basename(filepath))[0] self.set_current_module(modname, filepath) + # get the module representation - ast_node = self.get_ast(filepath, modname) - if ast_node is None: - continue - # XXX to be correct we need to keep module_msgs_state for every - # analyzed module (the problem stands with localized messages which - # are only detected in the .close step) - self.file_state = utils.FileState(descr["basename"]) - self._ignore_file = False - # fix the current file (if the source file was not available or - # if it's actually a c extension) - self.current_file = ast_node.file # pylint: disable=maybe-no-member - self.check_astroid_module(ast_node, walker, rawcheckers, tokencheckers) - # warn about spurious inline messages handling - spurious_messages = self.file_state.iter_spurious_suppression_messages( - self.msgs_store - ) - for msgid, line, args in spurious_messages: - self.add_message(msgid, line, None, args) + ast_node = _ast_from_string(_read_stdin(), filepath, modname) + + if ast_node is not None: + self.file_state = utils.FileState(filepath) + self.check_astroid_module(ast_node, walker, rawcheckers, tokencheckers) + # warn about spurious inline messages handling + spurious_messages = self.file_state.iter_spurious_suppression_messages( + self.msgs_store + ) + for msgid, line, args in spurious_messages: + self.add_message(msgid, line, None, args) + else: + for descr in self.expand_files(files_or_modules): + modname, filepath, is_arg = descr["name"], descr["path"], descr["isarg"] + if not self.should_analyze_file(modname, filepath, is_argument=is_arg): + continue + + self.set_current_module(modname, filepath) + # get the module representation + ast_node = self.get_ast(filepath, modname) + if ast_node is None: + continue + # XXX to be correct we need to keep module_msgs_state for every + # analyzed module (the problem stands with localized messages which + # are only detected in the .close step) + self.file_state = utils.FileState(descr["basename"]) + self._ignore_file = False + # fix the current file (if the source file was not available or + # if it's actually a c extension) + self.current_file = ast_node.file # pylint: disable=maybe-no-member + self.check_astroid_module(ast_node, walker, rawcheckers, tokencheckers) + # warn about spurious inline messages handling + spurious_messages = self.file_state.iter_spurious_suppression_messages( + self.msgs_store + ) + for msgid, line, args in spurious_messages: + self.add_message(msgid, line, None, args) # notify global end self.stats["statement"] = walker.nbstatements for checker in reversed(_checkers): diff --git a/pylint/test/test_self.py b/pylint/test/test_self.py index d03566eebf..224e1e44d0 100644 --- a/pylint/test/test_self.py +++ b/pylint/test/test_self.py @@ -30,6 +30,7 @@ import textwrap import configparser from io import StringIO +from unittest import mock from pylint.lint import Run from pylint.reporters import BaseReporter @@ -568,3 +569,79 @@ def test_parseable_file_path(self): finally: os.remove(module) os.removedirs(fake_path) + + @pytest.mark.parametrize( + "input_path,module,expected_path", + [ + (join(HERE, "mymodule.py"), "mymodule", "pylint/test/mymodule.py"), + ("mymodule.py", "mymodule", "mymodule.py"), + ], + ) + def test_stdin(self, input_path, module, expected_path): + expected_output = ( + "************* Module {module}\n" + "{path}:1:0: C0111: Missing module docstring (missing-docstring)\n" + "{path}:1:0: W0611: Unused import os (unused-import)\n\n" + ).format(path=expected_path, module=module) + + with mock.patch( + "pylint.lint._read_stdin", return_value="import os\n" + ) as mock_stdin: + self._test_output( + ["--from-stdin", input_path], expected_output=expected_output + ) + assert mock_stdin.call_count == 1 + + def test_stdin_missing_modulename(self): + self._runtest(["--from-stdin"], code=32) + + @pytest.mark.parametrize("write_bpy_to_disk", [False, True]) + def test_relative_imports(self, write_bpy_to_disk, tmpdir): + a = tmpdir.join("a") + + b_code = textwrap.dedent( + """ + from .c import foobar + from .d import bla # module does not exist + + foobar('hello') + bla() + """ + ) + + c_code = textwrap.dedent( + """ + def foobar(arg): + pass + """ + ) + + a.mkdir() + a.join("__init__.py").write("") + if write_bpy_to_disk: + a.join("b.py").write(b_code) + a.join("c.py").write(c_code) + + curdir = os.getcwd() + try: + # why don't we start pylint in a subprocess? + os.chdir(str(tmpdir)) + expected = ( + "************* Module a.b\n" + "a/b.py:1:0: C0111: Missing module docstring (missing-docstring)\n" + "a/b.py:3:0: E0401: Unable to import 'a.d' (import-error)\n\n" + ) + + if write_bpy_to_disk: + # --from-stdin is not used here + self._test_output(["a/b.py"], expected_output=expected) + + # this code needs to work w/ and w/o a file named a/b.py on the + # harddisk. + with mock.patch("pylint.lint._read_stdin", return_value=b_code): + self._test_output( + ["--from-stdin", join("a", "b.py")], expected_output=expected + ) + + finally: + os.chdir(curdir) From 55de04f2b01c794d031c5d8090b2b2a2b5a8ba77 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sat, 2 Mar 2019 14:29:40 +0100 Subject: [PATCH 0011/5147] Move the --from-stdin changelog to 2.4 --- ChangeLog | 11 ++++++----- doc/whatsnew/2.3.rst | 5 ----- doc/whatsnew/2.4.rst | 5 +++++ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 26eda11569..961a707b87 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,12 @@ Release date: TBA Close #2139 +* Support for linting file from stdin. + + IDEs may benefit from the support for linting from an in-memory file. + + Close #1187 + What's New in Pylint 2.3.0? =========================== @@ -148,11 +154,6 @@ Release date: 2019-02-27 * Fix false positive ``useless-else-on-loop`` if the break is deep in the else of an inner loop. -* Support for linting file from stdin. - - IDEs may benefit from the support for linting from an in-memory file. - - Close #1187 What's New in Pylint 2.2.2? =========================== diff --git a/doc/whatsnew/2.3.rst b/doc/whatsnew/2.3.rst index 4f4fa436b3..732618526d 100644 --- a/doc/whatsnew/2.3.rst +++ b/doc/whatsnew/2.3.rst @@ -65,8 +65,3 @@ For the full changes, check the Changelog. * There's a new command line option ``list-groups`` for listing all the check groups ``pylint`` knows about. This is useful to know what groups you can disable or enable individually. - -* A file is now read from stdin if the ``--from-stdin`` flag is used on the - command line. In addition to the ``--from-stdin`` flag a (single) file - name needs to be specified on the command line, which is needed for the - report. diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index c116721f2a..c232b54187 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -16,3 +16,8 @@ New checkers Other Changes ============= + +* A file is now read from stdin if the ``--from-stdin`` flag is used on the + command line. In addition to the ``--from-stdin`` flag a (single) file + name needs to be specified on the command line, which is needed for the + report. From 73c5c2fd4d5de0d38bebed7bd3688cb747225f39 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 3 Mar 2019 13:03:57 +0100 Subject: [PATCH 0012/5147] Include the missing __rfloordiv__. Close #2783 --- pylint/checkers/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index b4129389fd..dbbba49b0e 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -149,6 +149,7 @@ "__mul__", "__truediv__", "__floordiv__", + "__rfloordiv__", "__mod__", "__divmod__", "__lshift__", From 097227b612d3bac2bed5528b72ccea4309ff5c32 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 3 Mar 2019 13:27:37 +0100 Subject: [PATCH 0013/5147] Added a new check `class-variable-slots-conflict` This check is emitted when ``pylint`` finds a class variable that conflicts with a slot name, which would raise a ``ValueError`` at runtime. --- ChangeLog | 8 ++++++- doc/whatsnew/2.4.rst | 11 +++++++++ pylint/checkers/classes.py | 31 +++++++++++++++++-------- pylint/test/functional/slots_checks.py | 22 +++++++++++++++++- pylint/test/functional/slots_checks.txt | 3 +++ 5 files changed, 63 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 961a707b87..f6dd66bb90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,7 +9,7 @@ Release date: TBA * Added method arguments to the dot writer for pyreverse. -Close #2139 + Close #2139 * Support for linting file from stdin. @@ -17,6 +17,12 @@ Close #2139 Close #1187 +* Added a new check `class-variable-slots-conflict` + + This check is emitted when ``pylint`` finds a class variable that conflicts with a slot + name, which would raise a ``ValueError`` at runtime. + + What's New in Pylint 2.3.0? =========================== diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index c232b54187..2245d372ce 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -13,6 +13,17 @@ Summary -- Release highlights New checkers ============ +* A new check ``class-variable-slots-conflict`` was added. + + This check is emitted when ``pylint`` finds a class variable that conflicts with a slot + name, which would raise a ``ValueError`` at runtime. + + For example, the following would raise an error:: + + class A: + __slots__ = ('first', 'second') + first = 1 + Other Changes ============= diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 2fb152bd12..de255fe6a0 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -596,6 +596,11 @@ def _has_same_layout_slots(slots, assigned_value): "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", @@ -1050,27 +1055,33 @@ def _check_slots(self, node): values = slots.itered() if values is astroid.Uninferable: return - for elt in values: try: - self._check_slots_elt(elt) + self._check_slots_elt(elt, node) except astroid.InferenceError: continue - def _check_slots_elt(self, elt): - for infered in elt.infer(): - if infered is astroid.Uninferable: + def _check_slots_elt(self, elt, node): + for inferred in elt.infer(): + if inferred is astroid.Uninferable: continue - if not isinstance(infered, astroid.Const) or not isinstance( - infered.value, str + if not isinstance(inferred, astroid.Const) or not isinstance( + inferred.value, str ): self.add_message( - "invalid-slots-object", args=infered.as_string(), node=elt + "invalid-slots-object", args=inferred.as_string(), node=elt ) continue - if not infered.value: + 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: self.add_message( - "invalid-slots-object", args=infered.as_string(), node=elt + "class-variable-slots-conflict", args=(inferred.value,), node=elt ) def leave_functiondef(self, node): diff --git a/pylint/test/functional/slots_checks.py b/pylint/test/functional/slots_checks.py index 800a45e2fb..64a439f338 100644 --- a/pylint/test/functional/slots_checks.py +++ b/pylint/test/functional/slots_checks.py @@ -1,7 +1,7 @@ """ Checks that classes uses valid __slots__ """ # pylint: disable=too-few-public-methods, missing-docstring, no-absolute-import, useless-object-inheritance -# pylint: disable=using-constant-test, wrong-import-position, no-else-return +# pylint: disable=using-constant-test, wrong-import-position, no-else-return, line-too-long from collections import deque def func(): @@ -83,3 +83,23 @@ class PotentiallyThirdGood(object): class PotentiallyFourthGood(object): __slots__ = Good.__slots__ + + +class ValueInSlotConflict(object): + __slots__ = ('first', 'second', 'third', 'fourth') # [class-variable-slots-conflict, class-variable-slots-conflict, class-variable-slots-conflict] + first = None + + @property + def third(self): + return 42 + + def fourth(self): + return self.third + + +class Parent(object): + first = 42 + + +class ChildNotAffectedByValueInSlot(Parent): + __slots__ = ('first', ) diff --git a/pylint/test/functional/slots_checks.txt b/pylint/test/functional/slots_checks.txt index 90a6a80a84..b3d71afcc1 100644 --- a/pylint/test/functional/slots_checks.txt +++ b/pylint/test/functional/slots_checks.txt @@ -6,3 +6,6 @@ invalid-slots-object:49:FifthBad:"Invalid object ""''"" in __slots__, must conta single-string-used-for-slots:51:SixthBad:Class __slots__ should be a non-string iterable single-string-used-for-slots:54:SeventhBad:Class __slots__ should be a non-string iterable single-string-used-for-slots:57:EighthBad:Class __slots__ should be a non-string iterable +class-variable-slots-conflict:89:ValueInSlotConflict:Value 'first' in slots conflicts with class variable +class-variable-slots-conflict:89:ValueInSlotConflict:Value 'fourth' in slots conflicts with class variable +class-variable-slots-conflict:89:ValueInSlotConflict:Value 'third' in slots conflicts with class variable From 30a37b1150a2b7dd4b204b1263c2151cefb27ce6 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 3 Mar 2019 13:32:05 +0100 Subject: [PATCH 0014/5147] Add missing check_messages() for visit_classdef in classes checks --- pylint/checkers/classes.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index de255fe6a0..273cc47d14 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -728,6 +728,18 @@ def _dummy_rgx(self): 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 """ From 06b95515290bddba9765d9cfa11b6c9f03ec88d8 Mon Sep 17 00:00:00 2001 From: David Douard Date: Mon, 4 Mar 2019 10:14:52 +0100 Subject: [PATCH 0015/5147] Update the README's testing section (#2785) use a decent python version as tox cmd line example. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 189873f44f..d1eed03bff 100644 --- a/README.rst +++ b/README.rst @@ -95,7 +95,7 @@ We use tox_ for running the test suite. You should be able to install it with:: To run the test suite for a particular Python version, you can do:: - tox -e py27 + tox -e py37 For more detailed information, check the documentation. From 544e0a2133d7dcd339a94bdece11ccb8d32f0190 Mon Sep 17 00:00:00 2001 From: Dan Hemberger <846186+hemberger@users.noreply.github.com> Date: Mon, 4 Mar 2019 23:28:21 -0800 Subject: [PATCH 0016/5147] Set version to 2.4.0-dev0 (#2789) Make sure the master branch is tagged as a development version so that it is not mistaken for an official release. Also modify the version string logic to avoid misinterpreting development version 0 as an official release (since 0 == False) by using `dev_version = None` for non-development versions. --- pylint/__pkginfo__.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index fb1fc296c3..82caac1a4d 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -25,14 +25,13 @@ modname = distname = "pylint" +# For an official release, use dev_version = None numversion = (2, 4, 0) -dev_version = None -string_version = ".".join(str(num) for num in numversion) +dev_version = 0 -if dev_version: - version = string_version + "-dev" + str(dev_version) -else: - version = string_version +version = ".".join(str(num) for num in numversion) +if dev_version is not None: + version += "-dev" + str(dev_version) install_requires = ["astroid>=2.2.0,<3", "isort>=4.2.5,<5", "mccabe>=0.6,<0.7"] From a8e0f134796959ab08f029efe4de3a5843b5774c Mon Sep 17 00:00:00 2001 From: David Douard Date: Tue, 5 Mar 2019 08:28:42 +0100 Subject: [PATCH 0017/5147] Kill old and deprecated debian.sid directory (#2787) --- debian.sid/control | 57 ---------------------------------------- debian.sid/rules | 53 ------------------------------------- debian.sid/source/format | 1 - 3 files changed, 111 deletions(-) delete mode 100644 debian.sid/control delete mode 100755 debian.sid/rules delete mode 100644 debian.sid/source/format diff --git a/debian.sid/control b/debian.sid/control deleted file mode 100644 index 92d2316c79..0000000000 --- a/debian.sid/control +++ /dev/null @@ -1,57 +0,0 @@ -Source: pylint -Section: python -Priority: optional -Maintainer: Logilab S.A. -Uploaders: Sylvain Thénault , Alexandre Fayolle , - Sandro Tosi , Julien Jehannet -Build-Depends: debhelper (>= 7.0.50~), python-all, python3-all -Standards-Version: 3.9.2 -Homepage: http://www.logilab.org/project/pylint -Vcs-Hg: http://hg.logilab.org/pylint -Vcs-Browser: http://hg.logilab.org/pylint - -Package: pylint -Architecture: all -Depends: ${python:Depends}, ${misc:Depends}, python-logilab-common (>= 0.53), python-astroid -Recommends: python-tk, python-enchant -XB-Python-Version: ${python:Versions} -Description: python code static checker and UML diagram generator - Pylint is a Python source code analyzer which looks for programming - errors, helps enforcing a coding standard and sniffs for some code - smells (as defined in Martin Fowler's Refactoring book) - . - Pylint can be seen as another PyChecker since nearly all tests you - can do with PyChecker can also be done with Pylint. However, Pylint - offers some more features, like checking length of lines of code, - checking if variable names are well-formed according to your coding - standard, or checking if declared interfaces are truly implemented, - and much more. - . - Additionally, it is possible to write plugins to add your own checks. - . - The included command pyreverse generates UML class and package - diagrams. - . - -Package: pylint3 -Architecture: all -Depends: ${python3:Depends}, ${misc:Depends}, python3-logilab-common (>= 0.53), python3-astroid -Recommends: python3-tk, python3-enchant -XB-Python-Version: ${python3:Versions} -Description: python code static checker and UML diagram generator - Pylint is a Python source code analyzer which looks for programming - errors, helps enforcing a coding standard and sniffs for some code - smells (as defined in Martin Fowler's Refactoring book) - . - Pylint can be seen as another PyChecker since nearly all tests you - can do with PyChecker can also be done with Pylint. However, Pylint - offers some more features, like checking length of lines of code, - checking if variable names are well-formed according to your coding - standard, or checking if declared interfaces are truly implemented, - and much more. - . - Additionally, it is possible to write plugins to add your own checks. - . - The included command pyreverse generates UML class and package - diagrams. - . diff --git a/debian.sid/rules b/debian.sid/rules deleted file mode 100755 index 9a26fd63a1..0000000000 --- a/debian.sid/rules +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/make -f -# Sample debian/rules that uses debhelper. -# GNU copyright 1997 to 1999 by Joey Hess. -# -# adapted by Logilab for automatic generation by debianize -# (part of the devtools project, http://www.logilab.org/projects/devtools) -# -# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -include /usr/share/python/python.mk - -PYLIB:=$(call py_libdir,$(shell pyversions -d)) -PACKAGE:=$(call py_pkgname,pylint,python) -PYLIB3:=$(call py_libdir,python3.) -PACKAGE3:="pylint3" - -%: - dh $@ --with python2,python3 - -override_dh_install: - NO_SETUPTOOLS=1 python setup.py -q install --no-compile \ - --root=$(CURDIR)/debian/$(PACKAGE)/ \ - ${py_setup_install_args} - NO_SETUPTOOLS=1 python3 setup.py -q install --no-compile \ - --root=$(CURDIR)/debian/$(PACKAGE3)/ \ - ${py_setup_install_args} - # rename executables - for executable in pylint symilar epylint pyreverse ; do \ - new_exec="$$executable"3; \ - sed 's,^#!.*python$$,&3,' < $(CURDIR)/debian/$(PACKAGE3)/usr/bin/$$executable > $(CURDIR)/debian/$(PACKAGE3)/usr/bin/$$new_exec || exit 1; \ - done - # remove test directory - rm -rf debian/*/usr/lib/python*/*-packages/pylint/test - # generate pylint man page automatically - #PYTHONPATH=$(CURDIR)/debian/$(PACKAGE)/$(PYLIB) python $(CURDIR)/bin/pylint --generate-man > $(CURDIR)/man/pylint.1 - # rename and install man pages - dh_installman -ppylint man/*.1 - cd man && for i in *.1; do cp -v $$i "$$(basename $$i .1)3.1"; done - dh_installman -ppylint3 man/*3.1 - -override_dh_installdocs: - dh_installdocs README* - dh_installchangelogs -i ChangeLog - -override_dh_auto_test: -ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) - # PYTHON 2.X - # PYTHON 3.2 -endif diff --git a/debian.sid/source/format b/debian.sid/source/format deleted file mode 100644 index 163aaf8d82..0000000000 --- a/debian.sid/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) From 0ec1f532e96cad94efd02d18c030137bd7018cfa Mon Sep 17 00:00:00 2001 From: David Douard Date: Tue, 5 Mar 2019 08:29:42 +0100 Subject: [PATCH 0018/5147] Relicense the logo as CC-BY-SA-4.0 (#2786) and add the original svg file as well. --- ChangeLog | 1 + README.rst | 15 +- debian/copyright | 487 ++++++++++++++++++++++++++++++++++++++++++++--- doc/logo.svg | 267 ++++++++++++++++++++++++++ 4 files changed, 740 insertions(+), 30 deletions(-) create mode 100644 doc/logo.svg diff --git a/ChangeLog b/ChangeLog index f6dd66bb90..258b06e7b9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,7 @@ Release date: TBA This check is emitted when ``pylint`` finds a class variable that conflicts with a slot name, which would raise a ``ValueError`` at runtime. +* Relicense logo material under the CC BY-SA 4.0 license. What's New in Pylint 2.3.0? =========================== diff --git a/README.rst b/README.rst index d1eed03bff..95a08bcedc 100644 --- a/README.rst +++ b/README.rst @@ -39,7 +39,8 @@ from within your code, as well as from an extensive configuration file. It is also possible to write your own plugins for adding your own checks or for extending pylint in one way or another. -It's a free software distributed under the GNU General Public Licence. +It's a free software distributed under the GNU General Public Licence unless +otherwise specified. Development is hosted on GitHub: https://github.com/PyCQA/pylint/ @@ -102,3 +103,15 @@ For more detailed information, check the documentation. .. _here: http://pylint.pycqa.org/en/latest/user_guide/installation.html .. _tox: https://tox.readthedocs.io/en/latest/ + +License +------- + +pylint is, with a few exceptions listed below, `GPLv2 `_. + +The icon files are licensed under the `CC BY-SA 4.0 `_ license: + +- `doc/logo.png `_ +- `doc/logo.svg `_ + + diff --git a/debian/copyright b/debian/copyright index c2c1d7de62..ba6d96649c 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,34 +1,463 @@ -This package was debianized by Sylvain Thenault Sat, 13 Apr 2002 19:05:23 +0200. +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Source: https://github.com/PyCQA/pylint -It was downloaded from ftp://ftp.logilab.org/pub/pylint +Files: * +2003-2009, Sylvain Thenault +2003-2009, LOGILAB S.A. (Paris, FRANCE) +2009-2019, The pylint developers community +License: GPL-2+ -Upstream Author: +Files: doc/icon.* +Copyright: 2013, David Douard +License: CC-BY-SA-4.0 - Sylvain Thenault +License: GPL-2 -Copyright: + This program is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + . + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more + details. + . + You should have received a copy of the GNU General Public + License along with this package; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301 USA + . + On Debian systems, the full text of the GNU General Public + License version 2 can be found in the file + `/usr/share/common-licenses/GPL-2'. - Copyright (c) 2003-2009 Sylvain Thenault (thenault@gmail.com). - Copyright (c) 2003-2009 LOGILAB S.A. (Paris, FRANCE). - http://www.logilab.fr/ -- mailto:contact@logilab.fr - -License: - - This program is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free Software - Foundation; either version 2 of the License, or (at your option) any later - version. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - -On Debian systems, the complete text of the GNU General Public License v2 -may be found in '/usr/share/common-licenses/GPL-2'. - -The Debian packaging is Copyright (C) 2008-2009, Sandro Tosi -and is licensed under the same terms as upstream code (see above). +License: CC-BY-SA-4.0 + Attribution-ShareAlike 4.0 International + . + ======================================================================= + . + Creative Commons Corporation ("Creative Commons") is not a law firm and + does not provide legal services or legal advice. Distribution of + Creative Commons public licenses does not create a lawyer-client or + other relationship. Creative Commons makes its licenses and related + information available on an "as-is" basis. Creative Commons gives no + warranties regarding its licenses, any material licensed under their + terms and conditions, or any related information. Creative Commons + disclaims all liability for damages resulting from their use to the + fullest extent possible. + . + Using Creative Commons Public Licenses + . + Creative Commons public licenses provide a standard set of terms and + conditions that creators and other rights holders may use to share + original works of authorship and other material subject to copyright + and certain other rights specified in the public license below. The + following considerations are for informational purposes only, are not + exhaustive, and do not form part of our licenses. + . + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + . + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + . + ======================================================================= + . + Creative Commons Attribution-ShareAlike 4.0 International Public + License + . + By exercising the Licensed Rights (defined below), You accept and agree + to be bound by the terms and conditions of this Creative Commons + Attribution-ShareAlike 4.0 International Public License ("Public + License"). To the extent this Public License may be interpreted as a + contract, You are granted the Licensed Rights in consideration of Your + acceptance of these terms and conditions, and the Licensor grants You + such rights in consideration of benefits the Licensor receives from + making the Licensed Material available under these terms and + conditions. + . + . + Section 1 -- Definitions. + . + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + . + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + . + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + . + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + . + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + . + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + . + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + . + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + . + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + . + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + . + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + . + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + . + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + . + . + Section 2 -- Scope. + . + a. License grant. + . + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + . + a. reproduce and Share the Licensed Material, in whole or + in part; and + . + b. produce, reproduce, and Share Adapted Material. + . + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + . + 3. Term. The term of this Public License is specified in Section + 6(a). + . + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + . + 5. Downstream recipients. + . + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + . + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + . + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + . + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + . + b. Other rights. + . + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + . + 2. Patent and trademark rights are not licensed under this + Public License. + . + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + . + . + Section 3 -- License Conditions. + . + Your exercise of the Licensed Rights is expressly made subject to the + following conditions. + . + a. Attribution. + . + 1. If You Share the Licensed Material (including in modified + form), You must: + . + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + . + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + . + ii. a copyright notice; + . + iii. a notice that refers to this Public License; + . + iv. a notice that refers to the disclaimer of + warranties; + . + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + . + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + . + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + . + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + . + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + . + b. ShareAlike. + . + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + . + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + . + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + . + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + . + . + Section 4 -- Sui Generis Database Rights. + . + Where the Licensed Rights include Sui Generis Database Rights that + apply to Your use of the Licensed Material: + . + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + . + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + . + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + . + For the avoidance of doubt, this Section 4 supplements and does not + replace Your obligations under this Public License where the Licensed + Rights include other Copyright and Similar Rights. + . + . + Section 5 -- Disclaimer of Warranties and Limitation of Liability. + . + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + . + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + . + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + . + . + Section 6 -- Term and Termination. + . + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + . + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + . + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + . + 2. upon express reinstatement by the Licensor. + . + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + . + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + . + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + . + . + Section 7 -- Other Terms and Conditions. + . + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + . + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + . + . + Section 8 -- Interpretation. + . + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + . + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + . + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + . + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + . + . + ======================================================================= + . + Creative Commons is not a party to its public licenses. + Notwithstanding, Creative Commons may elect to apply one of its public + licenses to material it publishes and in those instances will be + considered the "Licensor." Except for the limited purpose of indicating + that material is shared under a Creative Commons public license or as + otherwise permitted by the Creative Commons policies published at + creativecommons.org/policies, Creative Commons does not authorize the + use of the trademark "Creative Commons" or any other trademark or logo + of Creative Commons without its prior written consent including, + without limitation, in connection with any unauthorized modifications + to any of its public licenses or any other arrangements, + understandings, or agreements concerning use of licensed material. For + the avoidance of doubt, this paragraph does not form part of the public + licenses. + . + Creative Commons may be contacted at creativecommons.org. + diff --git a/doc/logo.svg b/doc/logo.svg new file mode 100644 index 0000000000..e96e443963 --- /dev/null +++ b/doc/logo.svg @@ -0,0 +1,267 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 56eaf23ddc65381048212b9ae990056e2da81a54 Mon Sep 17 00:00:00 2001 From: Thomas Hisch Date: Tue, 5 Mar 2019 09:14:31 +0100 Subject: [PATCH 0019/5147] Fix output in python -m pylint --version (#2788) Close #2764 --- ChangeLog | 6 ++++++ pylint/lint.py | 8 ++++---- pylint/test/test_self.py | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 258b06e7b9..45526785e0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,8 +22,14 @@ Release date: TBA This check is emitted when ``pylint`` finds a class variable that conflicts with a slot name, which would raise a ``ValueError`` at runtime. + +* Fix issue with pylint name in output of python -m pylint --version + + Close #2764 + * Relicense logo material under the CC BY-SA 4.0 license. + What's New in Pylint 2.3.0? =========================== diff --git a/pylint/lint.py b/pylint/lint.py index 1b53b16589..0b273ea71c 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -46,15 +46,15 @@ # pylint: disable=broad-except -""" %prog [options] modules_or_packages +""" pylint [options] modules_or_packages Check that module(s) satisfy a coding standard (and more !). - %prog --help + pylint --help Display this help message and exit. - %prog --help-msg [,] + pylint --help-msg [,] Display help messages about given message identifiers and exit. """ @@ -615,7 +615,7 @@ def __init__(self, options=(), reporter=None, option_groups=(), pylintrc=None): "disable-msg": self.disable, "enable-msg": self.enable, } - full_version = "%%prog %s\nastroid %s\nPython %s" % ( + full_version = "pylint %s\nastroid %s\nPython %s" % ( version, astroid_version, sys.version, diff --git a/pylint/test/test_self.py b/pylint/test/test_self.py index 224e1e44d0..8fa262f17e 100644 --- a/pylint/test/test_self.py +++ b/pylint/test/test_self.py @@ -30,6 +30,7 @@ import textwrap import configparser from io import StringIO +import subprocess from unittest import mock from pylint.lint import Run @@ -645,3 +646,17 @@ def foobar(arg): finally: os.chdir(curdir) + + def test_version(self): + def check(lines): + assert lines[0].startswith("pylint ") + assert lines[1].startswith("astroid ") + assert lines[2].startswith("Python ") + + out = StringIO() + self._run_pylint(["--version"], out=out) + check(out.getvalue().splitlines()) + + result = subprocess.check_output([sys.executable, "-m", "pylint", "--version"]) + result = result.decode("utf-8") + check(result.splitlines()) From 21eb3914a3a102a92e970c4779e7708154ef7756 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Tue, 5 Mar 2019 09:45:05 +0100 Subject: [PATCH 0020/5147] Split deprecated_methods for Python 3.8 to account for removed methods --- .../test/functional/deprecated_methods_py3.rc | 1 + .../functional/deprecated_methods_py38.py | 46 +++++++++++++++++++ .../functional/deprecated_methods_py38.rc | 2 + .../functional/deprecated_methods_py38.txt | 14 ++++++ 4 files changed, 63 insertions(+) create mode 100644 pylint/test/functional/deprecated_methods_py38.py create mode 100644 pylint/test/functional/deprecated_methods_py38.rc create mode 100644 pylint/test/functional/deprecated_methods_py38.txt diff --git a/pylint/test/functional/deprecated_methods_py3.rc b/pylint/test/functional/deprecated_methods_py3.rc index 28c99bc284..6c3e72704c 100644 --- a/pylint/test/functional/deprecated_methods_py3.rc +++ b/pylint/test/functional/deprecated_methods_py3.rc @@ -1,2 +1,3 @@ [testoptions] min_pyver=3.3 +max_pyver=3.7 diff --git a/pylint/test/functional/deprecated_methods_py38.py b/pylint/test/functional/deprecated_methods_py38.py new file mode 100644 index 0000000000..951d6cc819 --- /dev/null +++ b/pylint/test/functional/deprecated_methods_py38.py @@ -0,0 +1,46 @@ +""" Functional tests for method deprecation. """ +# pylint: disable=missing-docstring, super-init-not-called, not-callable +import base64 +import inspect +import logging +import nntplib +import unittest +import xml.etree.ElementTree + + +class MyTest(unittest.TestCase): + def test(self): + self.assert_(True) # [deprecated-method] + +xml.etree.ElementTree.Element('tag').getchildren() # [deprecated-method] +xml.etree.ElementTree.Element('tag').getiterator() # [deprecated-method] +xml.etree.ElementTree.XMLParser('tag', None, None).doctype(None, None, None) # [deprecated-method] +nntplib.NNTP(None).xpath(None) # [deprecated-method] + + +inspect.getargspec(None) # [deprecated-method] +logging.warn("a") # [deprecated-method] +base64.encodestring("42") # [deprecated-method] +base64.decodestring("42") # [deprecated-method] + + +class SuperCrash(unittest.TestCase): + + def __init__(self): + # should not crash. + super(SuperCrash, self)() + +xml.etree.ElementTree.iterparse(None) + + +class Tests(unittest.TestCase): + + def test_foo(self): + self.assertEquals(2 + 2, 4) # [deprecated-method] + self.assertNotEquals(2 + 2, 4) # [deprecated-method] + self.assertAlmostEquals(2 + 2, 4) # [deprecated-method] + self.assertNotAlmostEquals(2 + 2, 4) # [deprecated-method] + self.assert_("abc" == "2") # [deprecated-method] + + self.assertRaisesRegex(ValueError, "exception") + self.assertRegex("something", r".+") diff --git a/pylint/test/functional/deprecated_methods_py38.rc b/pylint/test/functional/deprecated_methods_py38.rc new file mode 100644 index 0000000000..85fc502b37 --- /dev/null +++ b/pylint/test/functional/deprecated_methods_py38.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.8 diff --git a/pylint/test/functional/deprecated_methods_py38.txt b/pylint/test/functional/deprecated_methods_py38.txt new file mode 100644 index 0000000000..05b2309dd9 --- /dev/null +++ b/pylint/test/functional/deprecated_methods_py38.txt @@ -0,0 +1,14 @@ +deprecated-method:15:MyTest.test:Using deprecated method assert_() +deprecated-method:17::Using deprecated method getchildren() +deprecated-method:18::Using deprecated method getiterator() +deprecated-method:19::Using deprecated method doctype() +deprecated-method:20::Using deprecated method xpath() +deprecated-method:23::Using deprecated method getargspec() +deprecated-method:24::Using deprecated method warn() +deprecated-method:26::Using deprecated method encodestring() +deprecated-method:27::Using deprecated method decodestring() +deprecated-method:43:Tests.test_foo:Using deprecated method assertEquals() +deprecated-method:44:Tests.test_foo:Using deprecated method assertNotEquals() +deprecated-method:45:Tests.test_foo:Using deprecated method assertAlmostEquals() +deprecated-method:46:Tests.test_foo:Using deprecated method assertNotAlmostEquals() +deprecated-method:47:Tests.test_foo:Using deprecated method assert_() From 18b65c78c187cbb30b1bc73640e693817f34ecb4 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Tue, 5 Mar 2019 09:45:18 +0100 Subject: [PATCH 0021/5147] Expose Python 3.8 in tox --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 9972f22f47..d847ac7673 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py34, py35, py36, py37, pypy, pylint +envlist = py34, py35, py36, py37, py38, pypy, pylint skip_missing_interpreters = true [testenv:pylint] From 069749e085a1a89b534044cbb05e519fbd34f509 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Tue, 5 Mar 2019 09:47:52 +0100 Subject: [PATCH 0022/5147] Remove another method that's removed in 3.8 --- pylint/test/functional/deprecated_methods_py38.py | 1 - pylint/test/functional/deprecated_methods_py38.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/pylint/test/functional/deprecated_methods_py38.py b/pylint/test/functional/deprecated_methods_py38.py index 951d6cc819..4ca5e5197e 100644 --- a/pylint/test/functional/deprecated_methods_py38.py +++ b/pylint/test/functional/deprecated_methods_py38.py @@ -14,7 +14,6 @@ def test(self): xml.etree.ElementTree.Element('tag').getchildren() # [deprecated-method] xml.etree.ElementTree.Element('tag').getiterator() # [deprecated-method] -xml.etree.ElementTree.XMLParser('tag', None, None).doctype(None, None, None) # [deprecated-method] nntplib.NNTP(None).xpath(None) # [deprecated-method] diff --git a/pylint/test/functional/deprecated_methods_py38.txt b/pylint/test/functional/deprecated_methods_py38.txt index 05b2309dd9..10b56b94ec 100644 --- a/pylint/test/functional/deprecated_methods_py38.txt +++ b/pylint/test/functional/deprecated_methods_py38.txt @@ -1,7 +1,6 @@ deprecated-method:15:MyTest.test:Using deprecated method assert_() deprecated-method:17::Using deprecated method getchildren() deprecated-method:18::Using deprecated method getiterator() -deprecated-method:19::Using deprecated method doctype() deprecated-method:20::Using deprecated method xpath() deprecated-method:23::Using deprecated method getargspec() deprecated-method:24::Using deprecated method warn() From e37bb778c7d8889ced9b83ab8c66efa1608df175 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Tue, 5 Mar 2019 09:48:55 +0100 Subject: [PATCH 0023/5147] Add 3.8-dev in Travis --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index c7b2b08894..6ee6e78f9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,10 @@ jobs: - python: 3.7 env: TOXENV=py37 dist: xenial + - python: 3.8-dev + env: TOXENV=py38 + dist: xenial + sudo: true - python: 3.6 env: TOXENV=spelling - stage: tests-pypy From 64e39d3e1da57d48d0e2219227db8690b0a05b15 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Tue, 5 Mar 2019 09:51:05 +0100 Subject: [PATCH 0024/5147] Also adjust the line numbers --- .../functional/deprecated_methods_py38.txt | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pylint/test/functional/deprecated_methods_py38.txt b/pylint/test/functional/deprecated_methods_py38.txt index 10b56b94ec..819d4420b0 100644 --- a/pylint/test/functional/deprecated_methods_py38.txt +++ b/pylint/test/functional/deprecated_methods_py38.txt @@ -1,13 +1,13 @@ -deprecated-method:15:MyTest.test:Using deprecated method assert_() -deprecated-method:17::Using deprecated method getchildren() -deprecated-method:18::Using deprecated method getiterator() -deprecated-method:20::Using deprecated method xpath() -deprecated-method:23::Using deprecated method getargspec() -deprecated-method:24::Using deprecated method warn() -deprecated-method:26::Using deprecated method encodestring() -deprecated-method:27::Using deprecated method decodestring() -deprecated-method:43:Tests.test_foo:Using deprecated method assertEquals() -deprecated-method:44:Tests.test_foo:Using deprecated method assertNotEquals() -deprecated-method:45:Tests.test_foo:Using deprecated method assertAlmostEquals() -deprecated-method:46:Tests.test_foo:Using deprecated method assertNotAlmostEquals() -deprecated-method:47:Tests.test_foo:Using deprecated method assert_() +deprecated-method:13:MyTest.test:Using deprecated method assert_() +deprecated-method:15::Using deprecated method getchildren() +deprecated-method:16::Using deprecated method getiterator() +deprecated-method:17::Using deprecated method xpath() +deprecated-method:20::Using deprecated method getargspec() +deprecated-method:21::Using deprecated method warn() +deprecated-method:22::Using deprecated method encodestring() +deprecated-method:23::Using deprecated method decodestring() +deprecated-method:38:Tests.test_foo:Using deprecated method assertEquals() +deprecated-method:39:Tests.test_foo:Using deprecated method assertNotEquals() +deprecated-method:40:Tests.test_foo:Using deprecated method assertAlmostEquals() +deprecated-method:41:Tests.test_foo:Using deprecated method assertNotAlmostEquals() +deprecated-method:42:Tests.test_foo:Using deprecated method assert_() From 4f25b4e67917b8e9c422e5ee3bb8705ce691a5fc Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Fri, 8 Mar 2019 17:42:24 +0100 Subject: [PATCH 0025/5147] Do not add print as a keyword any longer for superfluous-parens. Found in #2801 --- pylint/checkers/format.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index 9f97798f33..d0d3ee5162 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -726,8 +726,6 @@ def new_line(self, tokens, line_end, line_start): def process_module(self, module): self._keywords_with_parens = set() - if "print_function" in module.future_imports: - self._keywords_with_parens.add("print") def _check_keyword_parentheses(self, tokens, start): """Check that there are not unnecessary parens after a keyword. From fb6d44472abf78530e7a75e2ae477dda25024608 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Fri, 8 Mar 2019 17:48:19 +0100 Subject: [PATCH 0026/5147] Skip emitting unused-argument for the parameter to process_module() --- pylint/checkers/format.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index d0d3ee5162..d1aa8e91bc 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -724,7 +724,7 @@ def new_line(self, tokens, line_end, line_start): self._lines[line_num] = line.split("\n")[0] self.check_lines(line, line_num) - def process_module(self, module): + def process_module(self, _module): self._keywords_with_parens = set() def _check_keyword_parentheses(self, tokens, start): From 34c6d16141edf5841aba5223815775d941484d6e Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 18 Dec 2018 20:29:52 +0100 Subject: [PATCH 0027/5147] Chore - Move utils.py into its own package --- pylint/test/{ => utils}/unittest_utils.py | 0 pylint/{utils.py => utils/__init__.py} | 15 ++++++--------- 2 files changed, 6 insertions(+), 9 deletions(-) rename pylint/test/{ => utils}/unittest_utils.py (100%) rename pylint/{utils.py => utils/__init__.py} (99%) diff --git a/pylint/test/unittest_utils.py b/pylint/test/utils/unittest_utils.py similarity index 100% rename from pylint/test/unittest_utils.py rename to pylint/test/utils/unittest_utils.py diff --git a/pylint/utils.py b/pylint/utils/__init__.py similarity index 99% rename from pylint/utils.py rename to pylint/utils/__init__.py index 921930d213..df09260d9b 100644 --- a/pylint/utils.py +++ b/pylint/utils/__init__.py @@ -44,22 +44,19 @@ import codecs import collections -from inspect import cleandoc import os -from os.path import dirname, basename, splitext, exists, isdir, join, normpath import re import sys +import textwrap import tokenize import warnings -import textwrap - -from astroid import nodes, Module -from astroid import modutils +from inspect import cleandoc +from os.path import basename, dirname, exists, isdir, join, normpath, splitext -from pylint.interfaces import IRawChecker, ITokenChecker, UNDEFINED, implements +from astroid import Module, modutils, nodes +from pylint.exceptions import EmptyReportError, InvalidMessageError, UnknownMessageError +from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements from pylint.reporters.ureports.nodes import Section -from pylint.exceptions import InvalidMessageError, UnknownMessageError, EmptyReportError - MSG_TYPES = { "I": "info", From 5f34108a1000131d5341297c0c511b486835b153 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 18 Dec 2018 22:51:30 +0100 Subject: [PATCH 0028/5147] Chore - Move content from __init__.py to utils.py --- pylint/utils/__init__.py | 1496 +------------------------------------- pylint/utils/utils.py | 1467 +++++++++++++++++++++++++++++++++++++ 2 files changed, 1500 insertions(+), 1463 deletions(-) create mode 100644 pylint/utils/utils.py diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index df09260d9b..4abed8c296 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -40,1468 +40,38 @@ """some various utilities and helper classes, most of them used in the main pylint class """ -from __future__ import print_function -import codecs -import collections -import os -import re -import sys -import textwrap -import tokenize -import warnings -from inspect import cleandoc -from os.path import basename, dirname, exists, isdir, join, normpath, splitext - -from astroid import Module, modutils, nodes -from pylint.exceptions import EmptyReportError, InvalidMessageError, UnknownMessageError -from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements -from pylint.reporters.ureports.nodes import Section - -MSG_TYPES = { - "I": "info", - "C": "convention", - "R": "refactor", - "W": "warning", - "E": "error", - "F": "fatal", -} -MSG_TYPES_LONG = {v: k for k, v in MSG_TYPES.items()} - -MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1} - -_MSG_ORDER = "EWRCIF" -MSG_STATE_SCOPE_CONFIG = 0 -MSG_STATE_SCOPE_MODULE = 1 -MSG_STATE_CONFIDENCE = 2 - -# Allow stopping after the first semicolon/hash encountered, -# so that an option can be continued with the reasons -# why it is active or disabled. -OPTION_RGX = re.compile(r"\s*#.*\bpylint:\s*([^;#]+)[;#]{0,1}") - -# The line/node distinction does not apply to fatal errors and reports. -_SCOPE_EXEMPT = "FR" - - -class WarningScope: - LINE = "line-based-msg" - NODE = "node-based-msg" - - -_MsgBase = collections.namedtuple( - "_MsgBase", - [ - "msg_id", - "symbol", - "msg", - "C", - "category", - "confidence", - "abspath", - "path", - "module", - "obj", - "line", - "column", - ], +from pylint.utils.utils import ( + MSG_STATE_CONFIDENCE, + MSG_STATE_SCOPE_CONFIG, + MSG_STATE_SCOPE_MODULE, + MSG_TYPES, + MSG_TYPES_LONG, + MSG_TYPES_STATUS, + OPTION_RGX, + PY_EXTS, + FileState, + Message, + MessageDefinition, + MessagesHandlerMixIn, + MessagesStore, + PyLintASTWalker, + ReportsHandlerMixIn, + WarningScope, + _basename_in_blacklist_re, + _check_csv, + _format_option_value, + _splitstrip, + _unquote, + build_message_def, + category_id, + decoding_stream, + deprecated_option, + expand_modules, + format_section, + get_global_option, + get_module_and_frameid, + register_plugins, + safe_decode, + tokenize_module, ) - - -class Message(_MsgBase): - """This class represent a message to be issued by the reporters""" - - def __new__(cls, msg_id, symbol, location, msg, confidence): - return _MsgBase.__new__( - cls, - msg_id, - symbol, - msg, - msg_id[0], - MSG_TYPES[msg_id[0]], - confidence, - *location - ) - - def format(self, template): - """Format the message according to the given template. - - The template format is the one of the format method : - cf. http://docs.python.org/2/library/string.html#formatstrings - """ - # For some reason, _asdict on derived namedtuples does not work with - # Python 3.4. Needs some investigation. - return template.format(**dict(zip(self._fields, self))) - - -def get_module_and_frameid(node): - """return the module name and the frame id in the module""" - frame = node.frame() - module, obj = "", [] - while frame: - if isinstance(frame, Module): - module = frame.name - else: - obj.append(getattr(frame, "name", "")) - try: - frame = frame.parent.frame() - except AttributeError: - frame = None - obj.reverse() - return module, ".".join(obj) - - -def category_id(cid): - cid = cid.upper() - if cid in MSG_TYPES: - return cid - return MSG_TYPES_LONG.get(cid) - - -def safe_decode(line, encoding, *args, **kwargs): - """return decoded line from encoding or decode with default encoding""" - try: - return line.decode(encoding or sys.getdefaultencoding(), *args, **kwargs) - except LookupError: - return line.decode(sys.getdefaultencoding(), *args, **kwargs) - - -def decoding_stream(stream, encoding, errors="strict"): - try: - reader_cls = codecs.getreader(encoding or sys.getdefaultencoding()) - except LookupError: - reader_cls = codecs.getreader(sys.getdefaultencoding()) - return reader_cls(stream, errors) - - -def tokenize_module(module): - with module.stream() as stream: - readline = stream.readline - return list(tokenize.tokenize(readline)) - - -def build_message_def(checker, msgid, msg_tuple): - if implements(checker, (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: - # messages should have a symbol, but for backward compatibility - # they may not. - (msg, descr) = msg_tuple - warnings.warn( - "[pylint 0.26] description of message %s doesn't include " - "a symbolic name" % msgid, - DeprecationWarning, - ) - symbol = None - options.setdefault("scope", default_scope) - return MessageDefinition(checker, msgid, msg, descr, symbol, **options) - - -class MessageDefinition: - def __init__( - self, - checker, - msgid, - msg, - descr, - symbol, - scope, - minversion=None, - maxversion=None, - old_names=None, - ): - self.checker = checker - if len(msgid) != 5: - raise InvalidMessageError("Invalid message id %r" % msgid) - if not msgid[0] in MSG_TYPES: - raise InvalidMessageError("Bad message type %s in %r" % (msgid[0], msgid)) - self.msgid = msgid - self.msg = msg - self.descr = descr - self.symbol = symbol - self.scope = scope - self.minversion = minversion - self.maxversion = maxversion - self.old_names = old_names or [] - - def __repr__(self): - return "MessageDefinition:{}".format(self.__dict__) - - def may_be_emitted(self): - """return True if message may be emitted using the current interpreter""" - if self.minversion is not None and self.minversion > sys.version_info: - return False - if self.maxversion is not None and self.maxversion <= sys.version_info: - return False - return True - - def format_help(self, checkerref=False): - """return the help string for the given message id""" - desc = self.descr - if checkerref: - desc += " This message belongs to the %s checker." % self.checker.name - title = self.msg - if self.symbol: - msgid = "%s (%s)" % (self.symbol, self.msgid) - else: - msgid = self.msgid - if self.minversion or self.maxversion: - restr = [] - if self.minversion: - restr.append("< %s" % ".".join([str(n) for n in self.minversion])) - if self.maxversion: - restr.append(">= %s" % ".".join([str(n) for n in self.maxversion])) - restr = " or ".join(restr) - if checkerref: - desc += " It can't be emitted when using Python %s." % restr - else: - desc += " This message can't be emitted when using Python %s." % restr - desc = _normalize_text(" ".join(desc.split()), indent=" ") - if title != "%s": - title = title.splitlines()[0] - - return ":%s: *%s*\n%s" % (msgid, title.rstrip(" "), desc) - return ":%s:\n%s" % (msgid, desc) - - -class MessagesHandlerMixIn: - """a mix-in class containing all the messages related methods for the main - lint class - """ - - __by_id_managed_msgs = [] # type: ignore - - def __init__(self): - self._msgs_state = {} - self.msg_status = 0 - - def _checker_messages(self, checker): - for known_checker in self._checkers[checker.lower()]: - for msgid in known_checker.msgs: - yield msgid - - @classmethod - def clear_by_id_managed_msgs(cls): - cls.__by_id_managed_msgs.clear() - - @classmethod - def get_by_id_managed_msgs(cls): - return cls.__by_id_managed_msgs - - def _register_by_id_managed_msg(self, msgid, line, is_disabled=True): - """If the msgid is a numeric one, then register it to inform the user - it could furnish instead a symbolic msgid.""" - try: - message_definitions = self.msgs_store.get_message_definitions(msgid) - for message_definition in message_definitions: - if msgid == message_definition.msgid: - MessagesHandlerMixIn.__by_id_managed_msgs.append( - ( - self.current_name, - message_definition.msgid, - message_definition.symbol, - line, - is_disabled, - ) - ) - except UnknownMessageError: - pass - - def disable(self, msgid, scope="package", line=None, ignore_unknown=False): - """don't output message of the given id""" - self._set_msg_status( - msgid, enable=False, scope=scope, line=line, ignore_unknown=ignore_unknown - ) - self._register_by_id_managed_msg(msgid, line) - - def enable(self, msgid, scope="package", line=None, ignore_unknown=False): - """reenable message of the given id""" - self._set_msg_status( - msgid, enable=True, scope=scope, line=line, ignore_unknown=ignore_unknown - ) - self._register_by_id_managed_msg(msgid, line, is_disabled=False) - - def _set_msg_status( - self, msgid, enable, scope="package", line=None, ignore_unknown=False - ): - assert scope in ("package", "module") - - if msgid == "all": - for _msgid in MSG_TYPES: - self._set_msg_status(_msgid, enable, scope, line, ignore_unknown) - if enable and not self._python3_porting_mode: - # Don't activate the python 3 porting checker if it wasn't activated explicitly. - self.disable("python3") - return - - # msgid is a category? - catid = category_id(msgid) - if catid is not None: - for _msgid in self.msgs_store._msgs_by_category.get(catid): - self._set_msg_status(_msgid, enable, scope, line) - return - - # msgid is a checker name? - if msgid.lower() in self._checkers: - msgs_store = self.msgs_store - for checker in self._checkers[msgid.lower()]: - for _msgid in checker.msgs: - if _msgid in msgs_store._alternative_names: - self._set_msg_status(_msgid, enable, scope, line) - return - - # msgid is report id? - if msgid.lower().startswith("rp"): - if enable: - self.enable_report(msgid) - else: - self.disable_report(msgid) - return - - try: - # msgid is a symbolic or numeric msgid. - message_definitions = self.msgs_store.get_message_definitions(msgid) - except UnknownMessageError: - if ignore_unknown: - return - raise - for message_definition in message_definitions: - self._set_one_msg_status(scope, message_definition, line, enable) - - def _set_one_msg_status(self, scope, msg, line, enable): - if scope == "module": - self.file_state.set_msg_status(msg, line, enable) - if not enable and msg.symbol != "locally-disabled": - self.add_message( - "locally-disabled", line=line, args=(msg.symbol, msg.msgid) - ) - else: - msgs = self._msgs_state - msgs[msg.msgid] = enable - # sync configuration object - self.config.enable = [ - self._message_symbol(mid) for mid, val in sorted(msgs.items()) if val - ] - self.config.disable = [ - self._message_symbol(mid) - for mid, val in sorted(msgs.items()) - if not val - ] - - def _message_symbol(self, msgid): - """Get the message symbol of the given message id - - Return the original message id if the message does not - exist. - """ - try: - return [md.symbol for md in self.msgs_store.get_message_definitions(msgid)] - except UnknownMessageError: - return msgid - - def get_message_state_scope(self, msgid, line=None, confidence=UNDEFINED): - """Returns the scope at which a message was enabled/disabled.""" - if self.config.confidence and confidence.name not in self.config.confidence: - return MSG_STATE_CONFIDENCE - try: - if line in self.file_state._module_msgs_state[msgid]: - return MSG_STATE_SCOPE_MODULE - except (KeyError, TypeError): - return MSG_STATE_SCOPE_CONFIG - return None - - def is_message_enabled(self, msg_descr, line=None, confidence=None): - """return true if the message associated to the given message id is - enabled - - msgid may be either a numeric or symbolic message id. - """ - if self.config.confidence and confidence: - if confidence.name not in self.config.confidence: - return False - try: - message_definitions = self.msgs_store.get_message_definitions(msg_descr) - msgids = [md.msgid for md in message_definitions] - except UnknownMessageError: - # The linter checks for messages that are not registered - # due to version mismatch, just treat them as message IDs - # for now. - msgids = [msg_descr] - for msgid in msgids: - if self.is_one_message_enabled(msgid, line): - return True - return False - - def is_one_message_enabled(self, msgid, line): - if line is None: - return self._msgs_state.get(msgid, True) - try: - return self.file_state._module_msgs_state[msgid][line] - except KeyError: - # Check if the message's line is after the maximum line existing in ast tree. - # This line won't appear in the ast tree and won't be referred in - #  self.file_state._module_msgs_state - # This happens for example with a commented line at the end of a module. - max_line_number = self.file_state.get_effective_max_line_number() - if max_line_number and line > max_line_number: - fallback = True - lines = self.file_state._raw_module_msgs_state.get(msgid, {}) - - # Doesn't consider scopes, as a disable can be in a different scope - # than that of the current line. - closest_lines = reversed( - [ - (message_line, enable) - for message_line, enable in lines.items() - if message_line <= line - ] - ) - last_line, is_enabled = next(closest_lines, (None, None)) - if last_line is not None: - fallback = is_enabled - - return self._msgs_state.get(msgid, fallback) - return self._msgs_state.get(msgid, True) - - def add_message( - self, - msg_descr, - line=None, - node=None, - args=None, - confidence=UNDEFINED, - col_offset=None, - ): - """Adds a message given by ID or name. - - If provided, the message string is expanded using args. - - AST checkers must provide the node argument (but may optionally - provide line if the line number is different), raw and token checkers - must provide the line argument. - """ - message_definitions = self.msgs_store.get_message_definitions(msg_descr) - for message_definition in message_definitions: - self.add_one_message( - message_definition, line, node, args, confidence, col_offset - ) - - def add_one_message( - self, message_definition, line, node, args, confidence, col_offset - ): - msgid = message_definition.msgid - # backward compatibility, message may not have a symbol - symbol = message_definition.symbol or msgid - # Fatal messages and reports are special, the node/scope distinction - # does not apply to them. - if msgid[0] not in _SCOPE_EXEMPT: - if message_definition.scope == WarningScope.LINE: - if line is None: - raise InvalidMessageError( - "Message %s must provide line, got None" % msgid - ) - if node is not None: - raise InvalidMessageError( - "Message %s must only provide line, " - "got line=%s, node=%s" % (msgid, line, node) - ) - elif message_definition.scope == WarningScope.NODE: - # Node-based warnings may provide an override line. - if node is None: - raise InvalidMessageError( - "Message %s must provide Node, got None" % msgid - ) - - if line is None and node is not None: - line = node.fromlineno - if col_offset is None and hasattr(node, "col_offset"): - col_offset = ( - node.col_offset - ) # XXX measured in bytes for utf-8, divide by two for chars? - - # should this message be displayed - if not self.is_message_enabled(msgid, line, confidence): - self.file_state.handle_ignored_message( - self.get_message_state_scope(msgid, line, confidence), - msgid, - line, - node, - args, - confidence, - ) - return - # update stats - msg_cat = MSG_TYPES[msgid[0]] - self.msg_status |= MSG_TYPES_STATUS[msgid[0]] - self.stats[msg_cat] += 1 - self.stats["by_module"][self.current_name][msg_cat] += 1 - try: - self.stats["by_msg"][symbol] += 1 - except KeyError: - self.stats["by_msg"][symbol] = 1 - # expand message ? - msg = message_definition.msg - if args: - msg %= args - # get module and object - if node is None: - module, obj = self.current_name, "" - abspath = self.current_file - else: - module, obj = get_module_and_frameid(node) - abspath = node.root().file - path = abspath.replace(self.reporter.path_strip_prefix, "", 1) - # add the message - self.reporter.handle_message( - Message( - msgid, - symbol, - (abspath, path, module, obj, line or 1, col_offset or 0), - msg, - confidence, - ) - ) - - def print_full_documentation(self, stream=None): - """output a full documentation in ReST format""" - if not stream: - stream = sys.stdout - - print("Pylint global options and switches", file=stream) - print("----------------------------------", file=stream) - print("", file=stream) - print("Pylint provides global options and switches.", file=stream) - print("", file=stream) - - by_checker = {} - for checker in self.get_checkers(): - if checker.name == "master": - if checker.options: - for section, options in checker.options_by_section(): - if section is None: - title = "General options" - else: - title = "%s options" % section.capitalize() - print(title, file=stream) - print("~" * len(title), file=stream) - _rest_format_section(stream, None, options) - print("", file=stream) - else: - name = checker.name - try: - by_checker[name]["options"] += checker.options_and_values() - by_checker[name]["msgs"].update(checker.msgs) - by_checker[name]["reports"] += checker.reports - except KeyError: - by_checker[name] = { - "options": list(checker.options_and_values()), - "msgs": dict(checker.msgs), - "reports": list(checker.reports), - } - - print("Pylint checkers' options and switches", file=stream) - print("-------------------------------------", file=stream) - print("", file=stream) - print("Pylint checkers can provide three set of features:", file=stream) - print("", file=stream) - print("* options that control their execution,", file=stream) - print("* messages that they can raise,", file=stream) - print("* reports that they can generate.", file=stream) - print("", file=stream) - print("Below is a list of all checkers and their features.", file=stream) - print("", file=stream) - - for checker, info in sorted(by_checker.items()): - self._print_checker_doc(checker, info, stream=stream) - - @staticmethod - def _print_checker_doc(checker_name, info, stream=None): - """Helper method for print_full_documentation. - - Also used by doc/exts/pylint_extensions.py. - """ - if not stream: - stream = sys.stdout - - doc = info.get("doc") - module = info.get("module") - msgs = info.get("msgs") - options = info.get("options") - reports = info.get("reports") - - checker_title = "%s checker" % (checker_name.replace("_", " ").title()) - - if module: - # Provide anchor to link against - print(".. _%s:\n" % module, file=stream) - print(checker_title, file=stream) - print("~" * len(checker_title), file=stream) - print("", file=stream) - if module: - print("This checker is provided by ``%s``." % module, file=stream) - print("Verbatim name of the checker is ``%s``." % checker_name, file=stream) - print("", file=stream) - if doc: - # Provide anchor to link against - title = "{} Documentation".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) - print(cleandoc(doc), file=stream) - print("", file=stream) - if options: - title = "{} Options".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) - _rest_format_section(stream, None, options) - print("", file=stream) - if msgs: - title = "{} Messages".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) - for msgid, msg in sorted( - msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) - ): - msg = build_message_def(checker_name, msgid, msg) - print(msg.format_help(checkerref=False), file=stream) - print("", file=stream) - if reports: - title = "{} Reports".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) - for report in reports: - print(":%s: %s" % report[:2], file=stream) - print("", file=stream) - print("", file=stream) - - -class FileState: - """Hold internal state specific to the currently analyzed file""" - - def __init__(self, modname=None): - self.base_name = modname - self._module_msgs_state = {} - self._raw_module_msgs_state = {} - self._ignored_msgs = collections.defaultdict(set) - self._suppression_mapping = {} - self._effective_max_line_number = None - - def collect_block_lines(self, msgs_store, module_node): - """Walk the AST to collect block level options line numbers.""" - for msg, lines in self._module_msgs_state.items(): - self._raw_module_msgs_state[msg] = lines.copy() - orig_state = self._module_msgs_state.copy() - self._module_msgs_state = {} - self._suppression_mapping = {} - self._effective_max_line_number = module_node.tolineno - self._collect_block_lines(msgs_store, module_node, orig_state) - - def _collect_block_lines(self, msgs_store, node, msg_state): - """Recursively walk (depth first) AST to collect block level options - line numbers. - """ - for child in node.get_children(): - self._collect_block_lines(msgs_store, child, msg_state) - first = node.fromlineno - last = node.tolineno - # first child line number used to distinguish between disable - # which are the first child of scoped node with those defined later. - # For instance in the code below: - # - # 1. def meth8(self): - # 2. """test late disabling""" - # 3. # pylint: disable=E1102 - # 4. print self.blip - # 5. # pylint: disable=E1101 - # 6. print self.bla - # - # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6 - # - # this is necessary to disable locally messages applying to class / - # function using their fromlineno - if ( - isinstance(node, (nodes.Module, nodes.ClassDef, nodes.FunctionDef)) - and node.body - ): - firstchildlineno = node.body[0].fromlineno - else: - firstchildlineno = last - for msgid, lines in msg_state.items(): - for lineno, state in list(lines.items()): - original_lineno = lineno - if first > lineno or last < lineno: - continue - # Set state for all lines for this block, if the - # warning is applied to nodes. - message_definitions = msgs_store.get_message_definitions(msgid) - for message_definition in message_definitions: - if message_definition.scope == WarningScope.NODE: - if lineno > firstchildlineno: - state = True - first_, last_ = node.block_range(lineno) - else: - first_ = lineno - last_ = last - for line in range(first_, last_ + 1): - # do not override existing entries - if line in self._module_msgs_state.get(msgid, ()): - continue - if line in lines: # state change in the same block - state = lines[line] - original_lineno = line - if not state: - self._suppression_mapping[(msgid, line)] = original_lineno - try: - self._module_msgs_state[msgid][line] = state - except KeyError: - self._module_msgs_state[msgid] = {line: state} - del lines[lineno] - - def set_msg_status(self, msg, line, status): - """Set status (enabled/disable) for a given message at a given line""" - assert line > 0 - try: - self._module_msgs_state[msg.msgid][line] = status - except KeyError: - self._module_msgs_state[msg.msgid] = {line: status} - - def handle_ignored_message( - self, state_scope, msgid, line, node, args, confidence - ): # pylint: disable=unused-argument - """Report an ignored message. - - state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG, - depending on whether the message was disabled locally in the module, - or globally. The other arguments are the same as for add_message. - """ - if state_scope == MSG_STATE_SCOPE_MODULE: - try: - orig_line = self._suppression_mapping[(msgid, line)] - self._ignored_msgs[(msgid, orig_line)].add(line) - except KeyError: - pass - - def iter_spurious_suppression_messages(self, msgs_store): - for warning, lines in self._raw_module_msgs_state.items(): - for line, enable in lines.items(): - if not enable and (warning, line) not in self._ignored_msgs: - yield "useless-suppression", line, ( - msgs_store.get_msg_display_string(warning), - ) - # don't use iteritems here, _ignored_msgs may be modified by add_message - for (warning, from_), lines in list(self._ignored_msgs.items()): - for line in lines: - yield "suppressed-message", line, ( - msgs_store.get_msg_display_string(warning), - from_, - ) - - def get_effective_max_line_number(self): - return self._effective_max_line_number - - -class MessagesStore: - """The messages store knows information about every possible message but has - no particular state during analysis. - """ - - def __init__(self): - # Primary registry for all active messages (i.e. all messages - # that can be emitted by pylint for the underlying Python - # version). It contains the 1:1 mapping from symbolic names - # to message definition objects. - # Keys are msg ids, values are a 2-uple with the msg type and the - # msg itself - self._messages_definitions = {} - # Maps alternative names (numeric IDs, deprecated names) to - # message definitions. May contain several names for each definition - # object. - self._alternative_names = {} - self._msgs_by_category = collections.defaultdict(list) - - @property - def messages(self): - """The list of all active messages.""" - return self._messages_definitions.values() - - def add_renamed_message(self, old_id, old_symbol, new_symbol): - """Register the old ID and symbol for a warning that was renamed. - - This allows users to keep using the old ID/symbol in suppressions. - """ - message_definition = self.get_message_definitions(new_symbol)[0] - message_definition.old_names.append((old_id, old_symbol)) - self._register_alternative_name(message_definition, old_id, old_symbol) - - @staticmethod - def get_checker_message_definitions(checker): - """Return the list of messages definitions for a checker. - - :param BaseChecker checker: - :rtype: list - :return: A list of MessageDefinition. - """ - message_definitions = [] - for msgid, msg_tuple in sorted(checker.msgs.items()): - message = build_message_def(checker, msgid, msg_tuple) - message_definitions.append(message) - return message_definitions - - def register_messages_from_checker(self, checker): - """Register all messages from a checker. - - :param BaseChecker checker: - """ - checker_message_definitions = self.get_checker_message_definitions(checker) - self._check_checker_consistency(checker_message_definitions) - for message_definition in checker_message_definitions: - self.register_message(message_definition) - - def register_message(self, message): - """Register a MessageDefinition with consistency in mind. - - :param MessageDefinition message: The message definition being added. - """ - self._check_id_and_symbol_consistency(message.msgid, message.symbol) - self._check_symbol(message.msgid, message.symbol) - self._check_msgid(message.msgid, message.symbol) - for old_name in message.old_names: - self._check_symbol(message.msgid, old_name[1]) - self._messages_definitions[message.symbol] = message - self._register_alternative_name(message, message.msgid, message.symbol) - for old_id, old_symbol in message.old_names: - self._register_alternative_name(message, old_id, old_symbol) - self._msgs_by_category[message.msgid[0]].append(message.msgid) - - @staticmethod - def _check_checker_consistency(messages): - """Check the msgid consistency in a list of messages definitions. - - 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. - - :param list messages: List of MessageDefinition. - :raises InvalidMessageError: If the checker id in the messages are not - always the same - """ - checker_id = None - existing_ids = [] - for message in 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 _register_alternative_name(self, msg, msgid, symbol): - """helper for register_message()""" - self._check_id_and_symbol_consistency(msgid, symbol) - self._alternative_names[msgid] = msg - self._alternative_names[symbol] = msg - - def _check_symbol(self, msgid, symbol): - """Check that a symbol is not already used. """ - other_message = self._messages_definitions.get(symbol) - if other_message: - self._raise_duplicate_msg_id(symbol, msgid, other_message.msgid) - else: - alternative_msgid = None - alternative_message = self._alternative_names.get(symbol) - if alternative_message: - if alternative_message.symbol == symbol: - alternative_msgid = alternative_message.msgid - else: - for old_msgid, old_symbol in alternative_message.old_names: - if old_symbol == symbol: - alternative_msgid = old_msgid - break - if msgid != alternative_msgid: - self._raise_duplicate_msg_id(symbol, msgid, alternative_msgid) - - def _check_msgid(self, msgid, symbol): - for message in self._messages_definitions.values(): - if message.msgid == msgid: - self._raise_duplicate_symbol(msgid, symbol, message.symbol) - - def _check_id_and_symbol_consistency(self, msgid, symbol): - try: - alternative = self._alternative_names[msgid] - except KeyError: - alternative = False - try: - if not alternative: - alternative = self._alternative_names[symbol] - except KeyError: - # There is no alternative names concerning this msgid/symbol. - # So nothing to check - return None - old_symbolic_name = None - old_symbolic_id = None - for alternate_msgid, alternate_symbol in alternative.old_names: - if alternate_msgid == msgid or alternate_symbol == symbol: - old_symbolic_id = alternate_msgid - old_symbolic_name = alternate_symbol - if symbol not in (alternative.symbol, old_symbolic_name): - if msgid == old_symbolic_id: - self._raise_duplicate_symbol(msgid, symbol, old_symbolic_name) - else: - self._raise_duplicate_symbol(msgid, symbol, alternative.symbol) - return None - - @staticmethod - def _raise_duplicate_symbol(msgid, symbol, other_symbol): - """Raise an error when a symbol is duplicated. - - :param str msgid: The msgid corresponding to the symbols - :param str symbol: Offending symbol - :param str other_symbol: Other offending symbol - :raises InvalidMessageError: when a symbol is duplicated. - """ - symbols = [symbol, other_symbol] - symbols.sort() - error_message = "Message id '{msgid}' cannot have both ".format(msgid=msgid) - error_message += "'{other_symbol}' and '{symbol}' as symbolic name.".format( - other_symbol=symbols[0], symbol=symbols[1] - ) - raise InvalidMessageError(error_message) - - @staticmethod - def _raise_duplicate_msg_id(symbol, msgid, other_msgid): - """Raise an error when a msgid is duplicated. - - :param str symbol: The symbol corresponding to the msgids - :param str msgid: Offending msgid - :param str other_msgid: Other offending msgid - :raises InvalidMessageError: when a msgid is duplicated. - """ - msgids = [msgid, other_msgid] - msgids.sort() - error_message = "Message symbol '{symbol}' cannot be used for ".format( - symbol=symbol - ) - error_message += "'{other_msgid}' and '{msgid}' at the same time.".format( - other_msgid=msgids[0], msgid=msgids[1] - ) - raise InvalidMessageError(error_message) - - def get_message_definitions(self, msgid_or_symbol: str) -> list: - """Returns the Message object for this message. - - :param str msgid_or_symbol: msgid_or_symbol may be either a numeric or symbolic id. - :raises UnknownMessageError: if the message id is not defined. - :rtype: List of MessageDefinition - :return: A message definition corresponding to msgid_or_symbol - """ - if msgid_or_symbol[1:].isdigit(): - msgid_or_symbol = msgid_or_symbol.upper() - for source in (self._alternative_names, self._messages_definitions): - try: - return [source[msgid_or_symbol]] - except KeyError: - pass - error_msg = "No such message id or symbol '{msgid_or_symbol}'.".format( - msgid_or_symbol=msgid_or_symbol - ) - raise UnknownMessageError(error_msg) - - def get_msg_display_string(self, msgid): - """Generates a user-consumable representation of a message. - - Can be just the message ID or the ID and the symbol. - """ - message_definitions = self.get_message_definitions(msgid) - if len(message_definitions) == 1: - return repr(message_definitions[0].symbol) - return repr([md.symbol for md in message_definitions]) - - def help_message(self, msgids): - """Display help messages for the given message identifiers""" - for msgid in msgids: - try: - for message_definition in self.get_message_definitions(msgid): - print(message_definition.format_help(checkerref=True)) - print("") - except UnknownMessageError as ex: - print(ex) - print("") - continue - - def list_messages(self): - """Output full messages list documentation in ReST format. """ - messages = sorted(self._messages_definitions.values(), key=lambda m: m.msgid) - for message in messages: - if not message.may_be_emitted(): - continue - print(message.format_help(checkerref=False)) - print("") - - -class ReportsHandlerMixIn: - """a mix-in class containing all the reports and stats manipulation - related methods for the main lint class - """ - - def __init__(self): - self._reports = collections.defaultdict(list) - self._reports_state = {} - - def report_order(self): - """ Return a list of reports, sorted in the order - in which they must be called. - """ - return list(self._reports) - - def register_report(self, reportid, r_title, r_cb, checker): - """register a report - - reportid is the unique identifier for the report - r_title the report's title - r_cb the method to call to make the report - checker is the checker defining the report - """ - reportid = reportid.upper() - self._reports[checker].append((reportid, r_title, r_cb)) - - def enable_report(self, reportid): - """disable the report of the given id""" - reportid = reportid.upper() - self._reports_state[reportid] = True - - def disable_report(self, reportid): - """disable the report of the given id""" - reportid = reportid.upper() - self._reports_state[reportid] = False - - def report_is_enabled(self, reportid): - """return true if the report associated to the given identifier is - enabled - """ - return self._reports_state.get(reportid, True) - - def make_reports(self, stats, old_stats): - """render registered reports""" - sect = Section("Report", "%s statements analysed." % (self.stats["statement"])) - for checker in self.report_order(): - for reportid, r_title, r_cb in self._reports[checker]: - if not self.report_is_enabled(reportid): - continue - report_sect = Section(r_title) - try: - r_cb(report_sect, stats, old_stats) - except EmptyReportError: - continue - report_sect.report_id = reportid - sect.append(report_sect) - return sect - - def add_stats(self, **kwargs): - """add some stats entries to the statistic dictionary - raise an AssertionError if there is a key conflict - """ - for key, value in kwargs.items(): - if key[-1] == "_": - key = key[:-1] - assert key not in self.stats - self.stats[key] = value - return self.stats - - -def _basename_in_blacklist_re(base_name, black_list_re): - """Determines if the basename is matched in a regex blacklist - - :param str base_name: The basename of the file - :param list black_list_re: A collection of regex patterns to match against. - Successful matches are blacklisted. - - :returns: `True` if the basename is blacklisted, `False` otherwise. - :rtype: bool - """ - for file_pattern in black_list_re: - if file_pattern.match(base_name): - return True - return False - - -def _modpath_from_file(filename, is_namespace): - def _is_package_cb(path, parts): - return modutils.check_modpath_has_init(path, parts) or is_namespace - - return modutils.modpath_from_file_with_callback( - filename, is_package_cb=_is_package_cb - ) - - -def expand_modules(files_or_modules, black_list, black_list_re): - """take a list of files/modules/packages and return the list of tuple - (file, module name) which have to be actually checked - """ - result = [] - errors = [] - for something in files_or_modules: - if os.path.basename(something) in black_list: - continue - if _basename_in_blacklist_re(os.path.basename(something), black_list_re): - continue - if exists(something): - # this is a file or a directory - try: - modname = ".".join(modutils.modpath_from_file(something)) - except ImportError: - modname = splitext(basename(something))[0] - if isdir(something): - filepath = join(something, "__init__.py") - else: - filepath = something - else: - # suppose it's a module or package - modname = something - try: - filepath = modutils.file_from_modpath(modname.split(".")) - if filepath is None: - continue - except (ImportError, SyntaxError) as ex: - # FIXME p3k : the SyntaxError is a Python bug and should be - # removed as soon as possible http://bugs.python.org/issue10588 - errors.append({"key": "fatal", "mod": modname, "ex": ex}) - continue - - filepath = normpath(filepath) - modparts = (modname or something).split(".") - - try: - spec = modutils.file_info_from_modpath(modparts, path=sys.path) - except ImportError: - # Might not be acceptable, don't crash. - is_namespace = False - is_directory = isdir(something) - else: - is_namespace = modutils.is_namespace(spec) - is_directory = modutils.is_directory(spec) - - if not is_namespace: - result.append( - { - "path": filepath, - "name": modname, - "isarg": True, - "basepath": filepath, - "basename": modname, - } - ) - - has_init = ( - not (modname.endswith(".__init__") or modname == "__init__") - and basename(filepath) == "__init__.py" - ) - - if has_init or is_namespace or is_directory: - for subfilepath in modutils.get_module_files( - dirname(filepath), black_list, list_all=is_namespace - ): - if filepath == subfilepath: - continue - if _basename_in_blacklist_re(basename(subfilepath), black_list_re): - continue - - modpath = _modpath_from_file(subfilepath, is_namespace) - submodname = ".".join(modpath) - result.append( - { - "path": subfilepath, - "name": submodname, - "isarg": False, - "basepath": filepath, - "basename": modname, - } - ) - return result, errors - - -class PyLintASTWalker: - def __init__(self, linter): - # callbacks per node types - self.nbstatements = 0 - self.visit_events = collections.defaultdict(list) - self.leave_events = collections.defaultdict(list) - self.linter = linter - - def _is_method_enabled(self, method): - if not hasattr(method, "checks_msgs"): - return True - for msg_desc in method.checks_msgs: - if self.linter.is_message_enabled(msg_desc): - return True - return False - - def add_checker(self, checker): - """walk to the checker's dir and collect visit and leave methods""" - # XXX : should be possible to merge needed_checkers and add_checker - vcids = set() - lcids = set() - visits = self.visit_events - leaves = self.leave_events - for member in dir(checker): - cid = member[6:] - if cid == "default": - continue - if member.startswith("visit_"): - v_meth = getattr(checker, member) - # don't use visit_methods with no activated message: - if self._is_method_enabled(v_meth): - visits[cid].append(v_meth) - vcids.add(cid) - elif member.startswith("leave_"): - l_meth = getattr(checker, member) - # don't use leave_methods with no activated message: - if self._is_method_enabled(l_meth): - leaves[cid].append(l_meth) - lcids.add(cid) - visit_default = getattr(checker, "visit_default", None) - if visit_default: - for cls in nodes.ALL_NODE_CLASSES: - cid = cls.__name__.lower() - if cid not in vcids: - visits[cid].append(visit_default) - # for now we have no "leave_default" method in Pylint - - def walk(self, astroid): - """call visit events of astroid checkers for the given node, recurse on - its children, then leave events. - """ - cid = astroid.__class__.__name__.lower() - - # Detect if the node is a new name for a deprecated alias. - # In this case, favour the methods for the deprecated - # alias if any, in order to maintain backwards - # compatibility. - visit_events = self.visit_events.get(cid, ()) - leave_events = self.leave_events.get(cid, ()) - - if astroid.is_statement: - self.nbstatements += 1 - # generate events for this node on each checker - for cb in visit_events or (): - cb(astroid) - # recurse on children - for child in astroid.get_children(): - self.walk(child) - for cb in leave_events or (): - cb(astroid) - - -PY_EXTS = (".py", ".pyc", ".pyo", ".pyw", ".so", ".dll") - - -def register_plugins(linter, directory): - """load all module and package in the given directory, looking for a - 'register' function in each one, used to register pylint checkers - """ - imported = {} - for filename in os.listdir(directory): - base, extension = splitext(filename) - if base in imported or base == "__pycache__": - continue - if ( - extension in PY_EXTS - and base != "__init__" - or (not extension and isdir(join(directory, base))) - ): - try: - module = modutils.load_module_from_file(join(directory, filename)) - except ValueError: - # empty module name (usually emacs auto-save files) - continue - except ImportError as exc: - print( - "Problem importing module %s: %s" % (filename, exc), file=sys.stderr - ) - else: - if hasattr(module, "register"): - module.register(linter) - imported[base] = 1 - - -def get_global_option(checker, option, default=None): - """ Retrieve an option defined by the given *checker* or - by all known option providers. - - It will look in the list of all options providers - until the given *option* will be found. - If the option wasn't found, the *default* value will be returned. - """ - # First, try in the given checker's config. - # After that, look in the options providers. - - try: - return getattr(checker.config, option.replace("-", "_")) - except AttributeError: - pass - for provider in checker.linter.options_providers: - for options in provider.options: - if options[0] == option: - return getattr(provider.config, option.replace("-", "_")) - return default - - -def deprecated_option( - shortname=None, opt_type=None, help_msg=None, deprecation_msg=None -): - def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument - if deprecation_msg: - sys.stderr.write(deprecation_msg % (optname,)) - - option = { - "help": help_msg, - "hide": True, - "type": opt_type, - "action": "callback", - "callback": _warn_deprecated, - "deprecated": True, - } - if shortname: - option["shortname"] = shortname - return option - - -def _splitstrip(string, sep=","): - """return a list of stripped string by splitting the string given as - argument on `sep` (',' by default). Empty string are discarded. - - >>> _splitstrip('a, b, c , 4,,') - ['a', 'b', 'c', '4'] - >>> _splitstrip('a') - ['a'] - >>> _splitstrip('a,\nb,\nc,') - ['a', 'b', 'c'] - - :type string: str or unicode - :param string: a csv line - - :type sep: str or unicode - :param sep: field separator, default to the comma (',') - - :rtype: str or unicode - :return: the unquoted string (or the input string if it wasn't quoted) - """ - return [word.strip() for word in string.split(sep) if word.strip()] - - -def _unquote(string): - """remove optional quotes (simple or double) from the string - - :type string: str or unicode - :param string: an optionally quoted string - - :rtype: str or unicode - :return: the unquoted string (or the input string if it wasn't quoted) - """ - if not string: - return string - if string[0] in "\"'": - string = string[1:] - if string[-1] in "\"'": - string = string[:-1] - return string - - -def _normalize_text(text, line_len=80, indent=""): - """Wrap the text on the given line length.""" - return "\n".join( - textwrap.wrap( - text, width=line_len, initial_indent=indent, subsequent_indent=indent - ) - ) - - -def _check_csv(value): - if isinstance(value, (list, tuple)): - return value - return _splitstrip(value) - - -def _comment(string): - """return string as a comment""" - lines = [line.strip() for line in string.splitlines()] - return "# " + ("%s# " % os.linesep).join(lines) - - -def _format_option_value(optdict, value): - """return the user input's value from a 'compiled' value""" - if isinstance(value, (list, tuple)): - value = ",".join(_format_option_value(optdict, item) for item in value) - elif isinstance(value, dict): - value = ",".join("%s:%s" % (k, v) for k, v in value.items()) - elif hasattr(value, "match"): # optdict.get('type') == 'regexp' - # compiled regexp - value = value.pattern - elif optdict.get("type") == "yn": - value = "yes" if value else "no" - elif isinstance(value, str) and value.isspace(): - value = "'%s'" % value - return value - - -def _ini_format_section(stream, section, options, doc=None): - """format an options section using the INI format""" - if doc: - print(_comment(doc), file=stream) - print("[%s]" % section, file=stream) - _ini_format(stream, options) - - -def _ini_format(stream, options): - """format options using the INI format""" - for optname, optdict, value in options: - value = _format_option_value(optdict, value) - help_opt = optdict.get("help") - if help_opt: - help_opt = _normalize_text(help_opt, line_len=79, indent="# ") - print(file=stream) - print(help_opt, file=stream) - else: - print(file=stream) - if value is None: - print("#%s=" % optname, file=stream) - else: - value = str(value).strip() - if re.match(r"^([\w-]+,)+[\w-]+$", str(value)): - separator = "\n " + " " * len(optname) - value = separator.join(x + "," for x in str(value).split(",")) - # remove trailing ',' from last element of the list - value = value[:-1] - print("%s=%s" % (optname, value), file=stream) - - -format_section = _ini_format_section - - -def _rest_format_section(stream, section, options, doc=None): - """format an options section using as ReST formatted output""" - if section: - print("%s\n%s" % (section, "'" * len(section)), file=stream) - if doc: - print(_normalize_text(doc, line_len=79, indent=""), file=stream) - print(file=stream) - for optname, optdict, value in options: - help_opt = optdict.get("help") - print(":%s:" % optname, file=stream) - if help_opt: - help_opt = _normalize_text(help_opt, line_len=79, indent=" ") - print(help_opt, file=stream) - if value: - value = str(_format_option_value(optdict, value)) - print(file=stream) - print(" Default: ``%s``" % value.replace("`` ", "```` ``"), file=stream) diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py new file mode 100644 index 0000000000..4b94567d6d --- /dev/null +++ b/pylint/utils/utils.py @@ -0,0 +1,1467 @@ +# -*- coding: utf-8 -*- + +from __future__ import print_function + +import codecs +import collections +import os +import re +import sys +import textwrap +import tokenize +import warnings +from inspect import cleandoc +from os.path import basename, dirname, exists, isdir, join, normpath, splitext + +from astroid import Module, modutils, nodes +from pylint.exceptions import EmptyReportError, InvalidMessageError, UnknownMessageError +from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements +from pylint.reporters.ureports.nodes import Section + +MSG_TYPES = { + "I": "info", + "C": "convention", + "R": "refactor", + "W": "warning", + "E": "error", + "F": "fatal", +} +MSG_TYPES_LONG = {v: k for k, v in MSG_TYPES.items()} + +MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1} + +_MSG_ORDER = "EWRCIF" +MSG_STATE_SCOPE_CONFIG = 0 +MSG_STATE_SCOPE_MODULE = 1 +MSG_STATE_CONFIDENCE = 2 + +# Allow stopping after the first semicolon/hash encountered, +# so that an option can be continued with the reasons +# why it is active or disabled. +OPTION_RGX = re.compile(r"\s*#.*\bpylint:\s*([^;#]+)[;#]{0,1}") + +# The line/node distinction does not apply to fatal errors and reports. +_SCOPE_EXEMPT = "FR" + + +class WarningScope: + LINE = "line-based-msg" + NODE = "node-based-msg" + + +_MsgBase = collections.namedtuple( + "_MsgBase", + [ + "msg_id", + "symbol", + "msg", + "C", + "category", + "confidence", + "abspath", + "path", + "module", + "obj", + "line", + "column", + ], +) + + +class Message(_MsgBase): + """This class represent a message to be issued by the reporters""" + + def __new__(cls, msg_id, symbol, location, msg, confidence): + return _MsgBase.__new__( + cls, + msg_id, + symbol, + msg, + msg_id[0], + MSG_TYPES[msg_id[0]], + confidence, + *location + ) + + def format(self, template): + """Format the message according to the given template. + + The template format is the one of the format method : + cf. http://docs.python.org/2/library/string.html#formatstrings + """ + # For some reason, _asdict on derived namedtuples does not work with + # Python 3.4. Needs some investigation. + return template.format(**dict(zip(self._fields, self))) + + +def get_module_and_frameid(node): + """return the module name and the frame id in the module""" + frame = node.frame() + module, obj = "", [] + while frame: + if isinstance(frame, Module): + module = frame.name + else: + obj.append(getattr(frame, "name", "")) + try: + frame = frame.parent.frame() + except AttributeError: + frame = None + obj.reverse() + return module, ".".join(obj) + + +def category_id(cid): + cid = cid.upper() + if cid in MSG_TYPES: + return cid + return MSG_TYPES_LONG.get(cid) + + +def safe_decode(line, encoding, *args, **kwargs): + """return decoded line from encoding or decode with default encoding""" + try: + return line.decode(encoding or sys.getdefaultencoding(), *args, **kwargs) + except LookupError: + return line.decode(sys.getdefaultencoding(), *args, **kwargs) + + +def decoding_stream(stream, encoding, errors="strict"): + try: + reader_cls = codecs.getreader(encoding or sys.getdefaultencoding()) + except LookupError: + reader_cls = codecs.getreader(sys.getdefaultencoding()) + return reader_cls(stream, errors) + + +def tokenize_module(module): + with module.stream() as stream: + readline = stream.readline + return list(tokenize.tokenize(readline)) + + +def build_message_def(checker, msgid, msg_tuple): + if implements(checker, (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: + # messages should have a symbol, but for backward compatibility + # they may not. + (msg, descr) = msg_tuple + warnings.warn( + "[pylint 0.26] description of message %s doesn't include " + "a symbolic name" % msgid, + DeprecationWarning, + ) + symbol = None + options.setdefault("scope", default_scope) + return MessageDefinition(checker, msgid, msg, descr, symbol, **options) + + +class MessageDefinition: + def __init__( + self, + checker, + msgid, + msg, + descr, + symbol, + scope, + minversion=None, + maxversion=None, + old_names=None, + ): + self.checker = checker + if len(msgid) != 5: + raise InvalidMessageError("Invalid message id %r" % msgid) + if not msgid[0] in MSG_TYPES: + raise InvalidMessageError("Bad message type %s in %r" % (msgid[0], msgid)) + self.msgid = msgid + self.msg = msg + self.descr = descr + self.symbol = symbol + self.scope = scope + self.minversion = minversion + self.maxversion = maxversion + self.old_names = old_names or [] + + def __repr__(self): + return "MessageDefinition:{}".format(self.__dict__) + + def may_be_emitted(self): + """return True if message may be emitted using the current interpreter""" + if self.minversion is not None and self.minversion > sys.version_info: + return False + if self.maxversion is not None and self.maxversion <= sys.version_info: + return False + return True + + def format_help(self, checkerref=False): + """return the help string for the given message id""" + desc = self.descr + if checkerref: + desc += " This message belongs to the %s checker." % self.checker.name + title = self.msg + if self.symbol: + msgid = "%s (%s)" % (self.symbol, self.msgid) + else: + msgid = self.msgid + if self.minversion or self.maxversion: + restr = [] + if self.minversion: + restr.append("< %s" % ".".join([str(n) for n in self.minversion])) + if self.maxversion: + restr.append(">= %s" % ".".join([str(n) for n in self.maxversion])) + restr = " or ".join(restr) + if checkerref: + desc += " It can't be emitted when using Python %s." % restr + else: + desc += " This message can't be emitted when using Python %s." % restr + desc = _normalize_text(" ".join(desc.split()), indent=" ") + if title != "%s": + title = title.splitlines()[0] + + return ":%s: *%s*\n%s" % (msgid, title.rstrip(" "), desc) + return ":%s:\n%s" % (msgid, desc) + + +class MessagesHandlerMixIn: + """a mix-in class containing all the messages related methods for the main + lint class + """ + + __by_id_managed_msgs = [] # type: ignore + + def __init__(self): + self._msgs_state = {} + self.msg_status = 0 + + def _checker_messages(self, checker): + for known_checker in self._checkers[checker.lower()]: + for msgid in known_checker.msgs: + yield msgid + + @classmethod + def clear_by_id_managed_msgs(cls): + cls.__by_id_managed_msgs.clear() + + @classmethod + def get_by_id_managed_msgs(cls): + return cls.__by_id_managed_msgs + + def _register_by_id_managed_msg(self, msgid, line, is_disabled=True): + """If the msgid is a numeric one, then register it to inform the user + it could furnish instead a symbolic msgid.""" + try: + message_definitions = self.msgs_store.get_message_definitions(msgid) + for message_definition in message_definitions: + if msgid == message_definition.msgid: + MessagesHandlerMixIn.__by_id_managed_msgs.append( + ( + self.current_name, + message_definition.msgid, + message_definition.symbol, + line, + is_disabled, + ) + ) + except UnknownMessageError: + pass + + def disable(self, msgid, scope="package", line=None, ignore_unknown=False): + """don't output message of the given id""" + self._set_msg_status( + msgid, enable=False, scope=scope, line=line, ignore_unknown=ignore_unknown + ) + self._register_by_id_managed_msg(msgid, line) + + def enable(self, msgid, scope="package", line=None, ignore_unknown=False): + """reenable message of the given id""" + self._set_msg_status( + msgid, enable=True, scope=scope, line=line, ignore_unknown=ignore_unknown + ) + self._register_by_id_managed_msg(msgid, line, is_disabled=False) + + def _set_msg_status( + self, msgid, enable, scope="package", line=None, ignore_unknown=False + ): + assert scope in ("package", "module") + + if msgid == "all": + for _msgid in MSG_TYPES: + self._set_msg_status(_msgid, enable, scope, line, ignore_unknown) + if enable and not self._python3_porting_mode: + # Don't activate the python 3 porting checker if it wasn't activated explicitly. + self.disable("python3") + return + + # msgid is a category? + catid = category_id(msgid) + if catid is not None: + for _msgid in self.msgs_store._msgs_by_category.get(catid): + self._set_msg_status(_msgid, enable, scope, line) + return + + # msgid is a checker name? + if msgid.lower() in self._checkers: + msgs_store = self.msgs_store + for checker in self._checkers[msgid.lower()]: + for _msgid in checker.msgs: + if _msgid in msgs_store._alternative_names: + self._set_msg_status(_msgid, enable, scope, line) + return + + # msgid is report id? + if msgid.lower().startswith("rp"): + if enable: + self.enable_report(msgid) + else: + self.disable_report(msgid) + return + + try: + # msgid is a symbolic or numeric msgid. + message_definitions = self.msgs_store.get_message_definitions(msgid) + except UnknownMessageError: + if ignore_unknown: + return + raise + for message_definition in message_definitions: + self._set_one_msg_status(scope, message_definition, line, enable) + + def _set_one_msg_status(self, scope, msg, line, enable): + if scope == "module": + self.file_state.set_msg_status(msg, line, enable) + if not enable and msg.symbol != "locally-disabled": + self.add_message( + "locally-disabled", line=line, args=(msg.symbol, msg.msgid) + ) + else: + msgs = self._msgs_state + msgs[msg.msgid] = enable + # sync configuration object + self.config.enable = [ + self._message_symbol(mid) for mid, val in sorted(msgs.items()) if val + ] + self.config.disable = [ + self._message_symbol(mid) + for mid, val in sorted(msgs.items()) + if not val + ] + + def _message_symbol(self, msgid): + """Get the message symbol of the given message id + + Return the original message id if the message does not + exist. + """ + try: + return [md.symbol for md in self.msgs_store.get_message_definitions(msgid)] + except UnknownMessageError: + return msgid + + def get_message_state_scope(self, msgid, line=None, confidence=UNDEFINED): + """Returns the scope at which a message was enabled/disabled.""" + if self.config.confidence and confidence.name not in self.config.confidence: + return MSG_STATE_CONFIDENCE + try: + if line in self.file_state._module_msgs_state[msgid]: + return MSG_STATE_SCOPE_MODULE + except (KeyError, TypeError): + return MSG_STATE_SCOPE_CONFIG + return None + + def is_message_enabled(self, msg_descr, line=None, confidence=None): + """return true if the message associated to the given message id is + enabled + + msgid may be either a numeric or symbolic message id. + """ + if self.config.confidence and confidence: + if confidence.name not in self.config.confidence: + return False + try: + message_definitions = self.msgs_store.get_message_definitions(msg_descr) + msgids = [md.msgid for md in message_definitions] + except UnknownMessageError: + # The linter checks for messages that are not registered + # due to version mismatch, just treat them as message IDs + # for now. + msgids = [msg_descr] + for msgid in msgids: + if self.is_one_message_enabled(msgid, line): + return True + return False + + def is_one_message_enabled(self, msgid, line): + if line is None: + return self._msgs_state.get(msgid, True) + try: + return self.file_state._module_msgs_state[msgid][line] + except KeyError: + # Check if the message's line is after the maximum line existing in ast tree. + # This line won't appear in the ast tree and won't be referred in + #  self.file_state._module_msgs_state + # This happens for example with a commented line at the end of a module. + max_line_number = self.file_state.get_effective_max_line_number() + if max_line_number and line > max_line_number: + fallback = True + lines = self.file_state._raw_module_msgs_state.get(msgid, {}) + + # Doesn't consider scopes, as a disable can be in a different scope + # than that of the current line. + closest_lines = reversed( + [ + (message_line, enable) + for message_line, enable in lines.items() + if message_line <= line + ] + ) + last_line, is_enabled = next(closest_lines, (None, None)) + if last_line is not None: + fallback = is_enabled + + return self._msgs_state.get(msgid, fallback) + return self._msgs_state.get(msgid, True) + + def add_message( + self, + msg_descr, + line=None, + node=None, + args=None, + confidence=UNDEFINED, + col_offset=None, + ): + """Adds a message given by ID or name. + + If provided, the message string is expanded using args. + + AST checkers must provide the node argument (but may optionally + provide line if the line number is different), raw and token checkers + must provide the line argument. + """ + message_definitions = self.msgs_store.get_message_definitions(msg_descr) + for message_definition in message_definitions: + self.add_one_message( + message_definition, line, node, args, confidence, col_offset + ) + + def add_one_message( + self, message_definition, line, node, args, confidence, col_offset + ): + msgid = message_definition.msgid + # backward compatibility, message may not have a symbol + symbol = message_definition.symbol or msgid + # Fatal messages and reports are special, the node/scope distinction + # does not apply to them. + if msgid[0] not in _SCOPE_EXEMPT: + if message_definition.scope == WarningScope.LINE: + if line is None: + raise InvalidMessageError( + "Message %s must provide line, got None" % msgid + ) + if node is not None: + raise InvalidMessageError( + "Message %s must only provide line, " + "got line=%s, node=%s" % (msgid, line, node) + ) + elif message_definition.scope == WarningScope.NODE: + # Node-based warnings may provide an override line. + if node is None: + raise InvalidMessageError( + "Message %s must provide Node, got None" % msgid + ) + + if line is None and node is not None: + line = node.fromlineno + if col_offset is None and hasattr(node, "col_offset"): + col_offset = ( + node.col_offset + ) # XXX measured in bytes for utf-8, divide by two for chars? + + # should this message be displayed + if not self.is_message_enabled(msgid, line, confidence): + self.file_state.handle_ignored_message( + self.get_message_state_scope(msgid, line, confidence), + msgid, + line, + node, + args, + confidence, + ) + return + # update stats + msg_cat = MSG_TYPES[msgid[0]] + self.msg_status |= MSG_TYPES_STATUS[msgid[0]] + self.stats[msg_cat] += 1 + self.stats["by_module"][self.current_name][msg_cat] += 1 + try: + self.stats["by_msg"][symbol] += 1 + except KeyError: + self.stats["by_msg"][symbol] = 1 + # expand message ? + msg = message_definition.msg + if args: + msg %= args + # get module and object + if node is None: + module, obj = self.current_name, "" + abspath = self.current_file + else: + module, obj = get_module_and_frameid(node) + abspath = node.root().file + path = abspath.replace(self.reporter.path_strip_prefix, "", 1) + # add the message + self.reporter.handle_message( + Message( + msgid, + symbol, + (abspath, path, module, obj, line or 1, col_offset or 0), + msg, + confidence, + ) + ) + + def print_full_documentation(self, stream=None): + """output a full documentation in ReST format""" + if not stream: + stream = sys.stdout + + print("Pylint global options and switches", file=stream) + print("----------------------------------", file=stream) + print("", file=stream) + print("Pylint provides global options and switches.", file=stream) + print("", file=stream) + + by_checker = {} + for checker in self.get_checkers(): + if checker.name == "master": + if checker.options: + for section, options in checker.options_by_section(): + if section is None: + title = "General options" + else: + title = "%s options" % section.capitalize() + print(title, file=stream) + print("~" * len(title), file=stream) + _rest_format_section(stream, None, options) + print("", file=stream) + else: + name = checker.name + try: + by_checker[name]["options"] += checker.options_and_values() + by_checker[name]["msgs"].update(checker.msgs) + by_checker[name]["reports"] += checker.reports + except KeyError: + by_checker[name] = { + "options": list(checker.options_and_values()), + "msgs": dict(checker.msgs), + "reports": list(checker.reports), + } + + print("Pylint checkers' options and switches", file=stream) + print("-------------------------------------", file=stream) + print("", file=stream) + print("Pylint checkers can provide three set of features:", file=stream) + print("", file=stream) + print("* options that control their execution,", file=stream) + print("* messages that they can raise,", file=stream) + print("* reports that they can generate.", file=stream) + print("", file=stream) + print("Below is a list of all checkers and their features.", file=stream) + print("", file=stream) + + for checker, info in sorted(by_checker.items()): + self._print_checker_doc(checker, info, stream=stream) + + @staticmethod + def _print_checker_doc(checker_name, info, stream=None): + """Helper method for print_full_documentation. + + Also used by doc/exts/pylint_extensions.py. + """ + if not stream: + stream = sys.stdout + + doc = info.get("doc") + module = info.get("module") + msgs = info.get("msgs") + options = info.get("options") + reports = info.get("reports") + + checker_title = "%s checker" % (checker_name.replace("_", " ").title()) + + if module: + # Provide anchor to link against + print(".. _%s:\n" % module, file=stream) + print(checker_title, file=stream) + print("~" * len(checker_title), file=stream) + print("", file=stream) + if module: + print("This checker is provided by ``%s``." % module, file=stream) + print("Verbatim name of the checker is ``%s``." % checker_name, file=stream) + print("", file=stream) + if doc: + # Provide anchor to link against + title = "{} Documentation".format(checker_title) + print(title, file=stream) + print("^" * len(title), file=stream) + print(cleandoc(doc), file=stream) + print("", file=stream) + if options: + title = "{} Options".format(checker_title) + print(title, file=stream) + print("^" * len(title), file=stream) + _rest_format_section(stream, None, options) + print("", file=stream) + if msgs: + title = "{} Messages".format(checker_title) + print(title, file=stream) + print("^" * len(title), file=stream) + for msgid, msg in sorted( + msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) + ): + msg = build_message_def(checker_name, msgid, msg) + print(msg.format_help(checkerref=False), file=stream) + print("", file=stream) + if reports: + title = "{} Reports".format(checker_title) + print(title, file=stream) + print("^" * len(title), file=stream) + for report in reports: + print(":%s: %s" % report[:2], file=stream) + print("", file=stream) + print("", file=stream) + + +class FileState: + """Hold internal state specific to the currently analyzed file""" + + def __init__(self, modname=None): + self.base_name = modname + self._module_msgs_state = {} + self._raw_module_msgs_state = {} + self._ignored_msgs = collections.defaultdict(set) + self._suppression_mapping = {} + self._effective_max_line_number = None + + def collect_block_lines(self, msgs_store, module_node): + """Walk the AST to collect block level options line numbers.""" + for msg, lines in self._module_msgs_state.items(): + self._raw_module_msgs_state[msg] = lines.copy() + orig_state = self._module_msgs_state.copy() + self._module_msgs_state = {} + self._suppression_mapping = {} + self._effective_max_line_number = module_node.tolineno + self._collect_block_lines(msgs_store, module_node, orig_state) + + def _collect_block_lines(self, msgs_store, node, msg_state): + """Recursively walk (depth first) AST to collect block level options + line numbers. + """ + for child in node.get_children(): + self._collect_block_lines(msgs_store, child, msg_state) + first = node.fromlineno + last = node.tolineno + # first child line number used to distinguish between disable + # which are the first child of scoped node with those defined later. + # For instance in the code below: + # + # 1. def meth8(self): + # 2. """test late disabling""" + # 3. # pylint: disable=E1102 + # 4. print self.blip + # 5. # pylint: disable=E1101 + # 6. print self.bla + # + # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6 + # + # this is necessary to disable locally messages applying to class / + # function using their fromlineno + if ( + isinstance(node, (nodes.Module, nodes.ClassDef, nodes.FunctionDef)) + and node.body + ): + firstchildlineno = node.body[0].fromlineno + else: + firstchildlineno = last + for msgid, lines in msg_state.items(): + for lineno, state in list(lines.items()): + original_lineno = lineno + if first > lineno or last < lineno: + continue + # Set state for all lines for this block, if the + # warning is applied to nodes. + message_definitions = msgs_store.get_message_definitions(msgid) + for message_definition in message_definitions: + if message_definition.scope == WarningScope.NODE: + if lineno > firstchildlineno: + state = True + first_, last_ = node.block_range(lineno) + else: + first_ = lineno + last_ = last + for line in range(first_, last_ + 1): + # do not override existing entries + if line in self._module_msgs_state.get(msgid, ()): + continue + if line in lines: # state change in the same block + state = lines[line] + original_lineno = line + if not state: + self._suppression_mapping[(msgid, line)] = original_lineno + try: + self._module_msgs_state[msgid][line] = state + except KeyError: + self._module_msgs_state[msgid] = {line: state} + del lines[lineno] + + def set_msg_status(self, msg, line, status): + """Set status (enabled/disable) for a given message at a given line""" + assert line > 0 + try: + self._module_msgs_state[msg.msgid][line] = status + except KeyError: + self._module_msgs_state[msg.msgid] = {line: status} + + def handle_ignored_message( + self, state_scope, msgid, line, node, args, confidence + ): # pylint: disable=unused-argument + """Report an ignored message. + + state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG, + depending on whether the message was disabled locally in the module, + or globally. The other arguments are the same as for add_message. + """ + if state_scope == MSG_STATE_SCOPE_MODULE: + try: + orig_line = self._suppression_mapping[(msgid, line)] + self._ignored_msgs[(msgid, orig_line)].add(line) + except KeyError: + pass + + def iter_spurious_suppression_messages(self, msgs_store): + for warning, lines in self._raw_module_msgs_state.items(): + for line, enable in lines.items(): + if not enable and (warning, line) not in self._ignored_msgs: + yield "useless-suppression", line, ( + msgs_store.get_msg_display_string(warning), + ) + # don't use iteritems here, _ignored_msgs may be modified by add_message + for (warning, from_), lines in list(self._ignored_msgs.items()): + for line in lines: + yield "suppressed-message", line, ( + msgs_store.get_msg_display_string(warning), + from_, + ) + + def get_effective_max_line_number(self): + return self._effective_max_line_number + + +class MessagesStore: + """The messages store knows information about every possible message but has + no particular state during analysis. + """ + + def __init__(self): + # Primary registry for all active messages (i.e. all messages + # that can be emitted by pylint for the underlying Python + # version). It contains the 1:1 mapping from symbolic names + # to message definition objects. + # Keys are msg ids, values are a 2-uple with the msg type and the + # msg itself + self._messages_definitions = {} + # Maps alternative names (numeric IDs, deprecated names) to + # message definitions. May contain several names for each definition + # object. + self._alternative_names = {} + self._msgs_by_category = collections.defaultdict(list) + + @property + def messages(self): + """The list of all active messages.""" + return self._messages_definitions.values() + + def add_renamed_message(self, old_id, old_symbol, new_symbol): + """Register the old ID and symbol for a warning that was renamed. + + This allows users to keep using the old ID/symbol in suppressions. + """ + message_definition = self.get_message_definitions(new_symbol)[0] + message_definition.old_names.append((old_id, old_symbol)) + self._register_alternative_name(message_definition, old_id, old_symbol) + + @staticmethod + def get_checker_message_definitions(checker): + """Return the list of messages definitions for a checker. + + :param BaseChecker checker: + :rtype: list + :return: A list of MessageDefinition. + """ + message_definitions = [] + for msgid, msg_tuple in sorted(checker.msgs.items()): + message = build_message_def(checker, msgid, msg_tuple) + message_definitions.append(message) + return message_definitions + + def register_messages_from_checker(self, checker): + """Register all messages from a checker. + + :param BaseChecker checker: + """ + checker_message_definitions = self.get_checker_message_definitions(checker) + self._check_checker_consistency(checker_message_definitions) + for message_definition in checker_message_definitions: + self.register_message(message_definition) + + def register_message(self, message): + """Register a MessageDefinition with consistency in mind. + + :param MessageDefinition message: The message definition being added. + """ + self._check_id_and_symbol_consistency(message.msgid, message.symbol) + self._check_symbol(message.msgid, message.symbol) + self._check_msgid(message.msgid, message.symbol) + for old_name in message.old_names: + self._check_symbol(message.msgid, old_name[1]) + self._messages_definitions[message.symbol] = message + self._register_alternative_name(message, message.msgid, message.symbol) + for old_id, old_symbol in message.old_names: + self._register_alternative_name(message, old_id, old_symbol) + self._msgs_by_category[message.msgid[0]].append(message.msgid) + + @staticmethod + def _check_checker_consistency(messages): + """Check the msgid consistency in a list of messages definitions. + + 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. + + :param list messages: List of MessageDefinition. + :raises InvalidMessageError: If the checker id in the messages are not + always the same + """ + checker_id = None + existing_ids = [] + for message in 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 _register_alternative_name(self, msg, msgid, symbol): + """helper for register_message()""" + self._check_id_and_symbol_consistency(msgid, symbol) + self._alternative_names[msgid] = msg + self._alternative_names[symbol] = msg + + def _check_symbol(self, msgid, symbol): + """Check that a symbol is not already used. """ + other_message = self._messages_definitions.get(symbol) + if other_message: + self._raise_duplicate_msg_id(symbol, msgid, other_message.msgid) + else: + alternative_msgid = None + alternative_message = self._alternative_names.get(symbol) + if alternative_message: + if alternative_message.symbol == symbol: + alternative_msgid = alternative_message.msgid + else: + for old_msgid, old_symbol in alternative_message.old_names: + if old_symbol == symbol: + alternative_msgid = old_msgid + break + if msgid != alternative_msgid: + self._raise_duplicate_msg_id(symbol, msgid, alternative_msgid) + + def _check_msgid(self, msgid, symbol): + for message in self._messages_definitions.values(): + if message.msgid == msgid: + self._raise_duplicate_symbol(msgid, symbol, message.symbol) + + def _check_id_and_symbol_consistency(self, msgid, symbol): + try: + alternative = self._alternative_names[msgid] + except KeyError: + alternative = False + try: + if not alternative: + alternative = self._alternative_names[symbol] + except KeyError: + # There is no alternative names concerning this msgid/symbol. + # So nothing to check + return None + old_symbolic_name = None + old_symbolic_id = None + for alternate_msgid, alternate_symbol in alternative.old_names: + if alternate_msgid == msgid or alternate_symbol == symbol: + old_symbolic_id = alternate_msgid + old_symbolic_name = alternate_symbol + if symbol not in (alternative.symbol, old_symbolic_name): + if msgid == old_symbolic_id: + self._raise_duplicate_symbol(msgid, symbol, old_symbolic_name) + else: + self._raise_duplicate_symbol(msgid, symbol, alternative.symbol) + return None + + @staticmethod + def _raise_duplicate_symbol(msgid, symbol, other_symbol): + """Raise an error when a symbol is duplicated. + + :param str msgid: The msgid corresponding to the symbols + :param str symbol: Offending symbol + :param str other_symbol: Other offending symbol + :raises InvalidMessageError: when a symbol is duplicated. + """ + symbols = [symbol, other_symbol] + symbols.sort() + error_message = "Message id '{msgid}' cannot have both ".format(msgid=msgid) + error_message += "'{other_symbol}' and '{symbol}' as symbolic name.".format( + other_symbol=symbols[0], symbol=symbols[1] + ) + raise InvalidMessageError(error_message) + + @staticmethod + def _raise_duplicate_msg_id(symbol, msgid, other_msgid): + """Raise an error when a msgid is duplicated. + + :param str symbol: The symbol corresponding to the msgids + :param str msgid: Offending msgid + :param str other_msgid: Other offending msgid + :raises InvalidMessageError: when a msgid is duplicated. + """ + msgids = [msgid, other_msgid] + msgids.sort() + error_message = "Message symbol '{symbol}' cannot be used for ".format( + symbol=symbol + ) + error_message += "'{other_msgid}' and '{msgid}' at the same time.".format( + other_msgid=msgids[0], msgid=msgids[1] + ) + raise InvalidMessageError(error_message) + + def get_message_definitions(self, msgid_or_symbol: str) -> list: + """Returns the Message object for this message. + + :param str msgid_or_symbol: msgid_or_symbol may be either a numeric or symbolic id. + :raises UnknownMessageError: if the message id is not defined. + :rtype: List of MessageDefinition + :return: A message definition corresponding to msgid_or_symbol + """ + if msgid_or_symbol[1:].isdigit(): + msgid_or_symbol = msgid_or_symbol.upper() + for source in (self._alternative_names, self._messages_definitions): + try: + return [source[msgid_or_symbol]] + except KeyError: + pass + error_msg = "No such message id or symbol '{msgid_or_symbol}'.".format( + msgid_or_symbol=msgid_or_symbol + ) + raise UnknownMessageError(error_msg) + + def get_msg_display_string(self, msgid): + """Generates a user-consumable representation of a message. + + Can be just the message ID or the ID and the symbol. + """ + message_definitions = self.get_message_definitions(msgid) + if len(message_definitions) == 1: + return repr(message_definitions[0].symbol) + return repr([md.symbol for md in message_definitions]) + + def help_message(self, msgids): + """Display help messages for the given message identifiers""" + for msgid in msgids: + try: + for message_definition in self.get_message_definitions(msgid): + print(message_definition.format_help(checkerref=True)) + print("") + except UnknownMessageError as ex: + print(ex) + print("") + continue + + def list_messages(self): + """Output full messages list documentation in ReST format. """ + messages = sorted(self._messages_definitions.values(), key=lambda m: m.msgid) + for message in messages: + if not message.may_be_emitted(): + continue + print(message.format_help(checkerref=False)) + print("") + + +class ReportsHandlerMixIn: + """a mix-in class containing all the reports and stats manipulation + related methods for the main lint class + """ + + def __init__(self): + self._reports = collections.defaultdict(list) + self._reports_state = {} + + def report_order(self): + """ Return a list of reports, sorted in the order + in which they must be called. + """ + return list(self._reports) + + def register_report(self, reportid, r_title, r_cb, checker): + """register a report + + reportid is the unique identifier for the report + r_title the report's title + r_cb the method to call to make the report + checker is the checker defining the report + """ + reportid = reportid.upper() + self._reports[checker].append((reportid, r_title, r_cb)) + + def enable_report(self, reportid): + """disable the report of the given id""" + reportid = reportid.upper() + self._reports_state[reportid] = True + + def disable_report(self, reportid): + """disable the report of the given id""" + reportid = reportid.upper() + self._reports_state[reportid] = False + + def report_is_enabled(self, reportid): + """return true if the report associated to the given identifier is + enabled + """ + return self._reports_state.get(reportid, True) + + def make_reports(self, stats, old_stats): + """render registered reports""" + sect = Section("Report", "%s statements analysed." % (self.stats["statement"])) + for checker in self.report_order(): + for reportid, r_title, r_cb in self._reports[checker]: + if not self.report_is_enabled(reportid): + continue + report_sect = Section(r_title) + try: + r_cb(report_sect, stats, old_stats) + except EmptyReportError: + continue + report_sect.report_id = reportid + sect.append(report_sect) + return sect + + def add_stats(self, **kwargs): + """add some stats entries to the statistic dictionary + raise an AssertionError if there is a key conflict + """ + for key, value in kwargs.items(): + if key[-1] == "_": + key = key[:-1] + assert key not in self.stats + self.stats[key] = value + return self.stats + + +def _basename_in_blacklist_re(base_name, black_list_re): + """Determines if the basename is matched in a regex blacklist + + :param str base_name: The basename of the file + :param list black_list_re: A collection of regex patterns to match against. + Successful matches are blacklisted. + + :returns: `True` if the basename is blacklisted, `False` otherwise. + :rtype: bool + """ + for file_pattern in black_list_re: + if file_pattern.match(base_name): + return True + return False + + +def _modpath_from_file(filename, is_namespace): + def _is_package_cb(path, parts): + return modutils.check_modpath_has_init(path, parts) or is_namespace + + return modutils.modpath_from_file_with_callback( + filename, is_package_cb=_is_package_cb + ) + + +def expand_modules(files_or_modules, black_list, black_list_re): + """take a list of files/modules/packages and return the list of tuple + (file, module name) which have to be actually checked + """ + result = [] + errors = [] + for something in files_or_modules: + if os.path.basename(something) in black_list: + continue + if _basename_in_blacklist_re(os.path.basename(something), black_list_re): + continue + if exists(something): + # this is a file or a directory + try: + modname = ".".join(modutils.modpath_from_file(something)) + except ImportError: + modname = splitext(basename(something))[0] + if isdir(something): + filepath = join(something, "__init__.py") + else: + filepath = something + else: + # suppose it's a module or package + modname = something + try: + filepath = modutils.file_from_modpath(modname.split(".")) + if filepath is None: + continue + except (ImportError, SyntaxError) as ex: + # FIXME p3k : the SyntaxError is a Python bug and should be + # removed as soon as possible http://bugs.python.org/issue10588 + errors.append({"key": "fatal", "mod": modname, "ex": ex}) + continue + + filepath = normpath(filepath) + modparts = (modname or something).split(".") + + try: + spec = modutils.file_info_from_modpath(modparts, path=sys.path) + except ImportError: + # Might not be acceptable, don't crash. + is_namespace = False + is_directory = isdir(something) + else: + is_namespace = modutils.is_namespace(spec) + is_directory = modutils.is_directory(spec) + + if not is_namespace: + result.append( + { + "path": filepath, + "name": modname, + "isarg": True, + "basepath": filepath, + "basename": modname, + } + ) + + has_init = ( + not (modname.endswith(".__init__") or modname == "__init__") + and basename(filepath) == "__init__.py" + ) + + if has_init or is_namespace or is_directory: + for subfilepath in modutils.get_module_files( + dirname(filepath), black_list, list_all=is_namespace + ): + if filepath == subfilepath: + continue + if _basename_in_blacklist_re(basename(subfilepath), black_list_re): + continue + + modpath = _modpath_from_file(subfilepath, is_namespace) + submodname = ".".join(modpath) + result.append( + { + "path": subfilepath, + "name": submodname, + "isarg": False, + "basepath": filepath, + "basename": modname, + } + ) + return result, errors + + +class PyLintASTWalker: + def __init__(self, linter): + # callbacks per node types + self.nbstatements = 0 + self.visit_events = collections.defaultdict(list) + self.leave_events = collections.defaultdict(list) + self.linter = linter + + def _is_method_enabled(self, method): + if not hasattr(method, "checks_msgs"): + return True + for msg_desc in method.checks_msgs: + if self.linter.is_message_enabled(msg_desc): + return True + return False + + def add_checker(self, checker): + """walk to the checker's dir and collect visit and leave methods""" + # XXX : should be possible to merge needed_checkers and add_checker + vcids = set() + lcids = set() + visits = self.visit_events + leaves = self.leave_events + for member in dir(checker): + cid = member[6:] + if cid == "default": + continue + if member.startswith("visit_"): + v_meth = getattr(checker, member) + # don't use visit_methods with no activated message: + if self._is_method_enabled(v_meth): + visits[cid].append(v_meth) + vcids.add(cid) + elif member.startswith("leave_"): + l_meth = getattr(checker, member) + # don't use leave_methods with no activated message: + if self._is_method_enabled(l_meth): + leaves[cid].append(l_meth) + lcids.add(cid) + visit_default = getattr(checker, "visit_default", None) + if visit_default: + for cls in nodes.ALL_NODE_CLASSES: + cid = cls.__name__.lower() + if cid not in vcids: + visits[cid].append(visit_default) + # for now we have no "leave_default" method in Pylint + + def walk(self, astroid): + """call visit events of astroid checkers for the given node, recurse on + its children, then leave events. + """ + cid = astroid.__class__.__name__.lower() + + # Detect if the node is a new name for a deprecated alias. + # In this case, favour the methods for the deprecated + # alias if any, in order to maintain backwards + # compatibility. + visit_events = self.visit_events.get(cid, ()) + leave_events = self.leave_events.get(cid, ()) + + if astroid.is_statement: + self.nbstatements += 1 + # generate events for this node on each checker + for cb in visit_events or (): + cb(astroid) + # recurse on children + for child in astroid.get_children(): + self.walk(child) + for cb in leave_events or (): + cb(astroid) + + +PY_EXTS = (".py", ".pyc", ".pyo", ".pyw", ".so", ".dll") + + +def register_plugins(linter, directory): + """load all module and package in the given directory, looking for a + 'register' function in each one, used to register pylint checkers + """ + imported = {} + for filename in os.listdir(directory): + base, extension = splitext(filename) + if base in imported or base == "__pycache__": + continue + if ( + extension in PY_EXTS + and base != "__init__" + or (not extension and isdir(join(directory, base))) + ): + try: + module = modutils.load_module_from_file(join(directory, filename)) + except ValueError: + # empty module name (usually emacs auto-save files) + continue + except ImportError as exc: + print( + "Problem importing module %s: %s" % (filename, exc), file=sys.stderr + ) + else: + if hasattr(module, "register"): + module.register(linter) + imported[base] = 1 + + +def get_global_option(checker, option, default=None): + """ Retrieve an option defined by the given *checker* or + by all known option providers. + + It will look in the list of all options providers + until the given *option* will be found. + If the option wasn't found, the *default* value will be returned. + """ + # First, try in the given checker's config. + # After that, look in the options providers. + + try: + return getattr(checker.config, option.replace("-", "_")) + except AttributeError: + pass + for provider in checker.linter.options_providers: + for options in provider.options: + if options[0] == option: + return getattr(provider.config, option.replace("-", "_")) + return default + + +def deprecated_option( + shortname=None, opt_type=None, help_msg=None, deprecation_msg=None +): + def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument + if deprecation_msg: + sys.stderr.write(deprecation_msg % (optname,)) + + option = { + "help": help_msg, + "hide": True, + "type": opt_type, + "action": "callback", + "callback": _warn_deprecated, + "deprecated": True, + } + if shortname: + option["shortname"] = shortname + return option + + +def _splitstrip(string, sep=","): + """return a list of stripped string by splitting the string given as + argument on `sep` (',' by default). Empty string are discarded. + + >>> _splitstrip('a, b, c , 4,,') + ['a', 'b', 'c', '4'] + >>> _splitstrip('a') + ['a'] + >>> _splitstrip('a,\nb,\nc,') + ['a', 'b', 'c'] + + :type string: str or unicode + :param string: a csv line + + :type sep: str or unicode + :param sep: field separator, default to the comma (',') + + :rtype: str or unicode + :return: the unquoted string (or the input string if it wasn't quoted) + """ + return [word.strip() for word in string.split(sep) if word.strip()] + + +def _unquote(string): + """remove optional quotes (simple or double) from the string + + :type string: str or unicode + :param string: an optionally quoted string + + :rtype: str or unicode + :return: the unquoted string (or the input string if it wasn't quoted) + """ + if not string: + return string + if string[0] in "\"'": + string = string[1:] + if string[-1] in "\"'": + string = string[:-1] + return string + + +def _normalize_text(text, line_len=80, indent=""): + """Wrap the text on the given line length.""" + return "\n".join( + textwrap.wrap( + text, width=line_len, initial_indent=indent, subsequent_indent=indent + ) + ) + + +def _check_csv(value): + if isinstance(value, (list, tuple)): + return value + return _splitstrip(value) + + +def _comment(string): + """return string as a comment""" + lines = [line.strip() for line in string.splitlines()] + return "# " + ("%s# " % os.linesep).join(lines) + + +def _format_option_value(optdict, value): + """return the user input's value from a 'compiled' value""" + if isinstance(value, (list, tuple)): + value = ",".join(_format_option_value(optdict, item) for item in value) + elif isinstance(value, dict): + value = ",".join("%s:%s" % (k, v) for k, v in value.items()) + elif hasattr(value, "match"): # optdict.get('type') == 'regexp' + # compiled regexp + value = value.pattern + elif optdict.get("type") == "yn": + value = "yes" if value else "no" + elif isinstance(value, str) and value.isspace(): + value = "'%s'" % value + return value + + +def _ini_format_section(stream, section, options, doc=None): + """format an options section using the INI format""" + if doc: + print(_comment(doc), file=stream) + print("[%s]" % section, file=stream) + _ini_format(stream, options) + + +def _ini_format(stream, options): + """format options using the INI format""" + for optname, optdict, value in options: + value = _format_option_value(optdict, value) + help_opt = optdict.get("help") + if help_opt: + help_opt = _normalize_text(help_opt, line_len=79, indent="# ") + print(file=stream) + print(help_opt, file=stream) + else: + print(file=stream) + if value is None: + print("#%s=" % optname, file=stream) + else: + value = str(value).strip() + if re.match(r"^([\w-]+,)+[\w-]+$", str(value)): + separator = "\n " + " " * len(optname) + value = separator.join(x + "," for x in str(value).split(",")) + # remove trailing ',' from last element of the list + value = value[:-1] + print("%s=%s" % (optname, value), file=stream) + + +format_section = _ini_format_section + + +def _rest_format_section(stream, section, options, doc=None): + """format an options section using as ReST formatted output""" + if section: + print("%s\n%s" % (section, "'" * len(section)), file=stream) + if doc: + print(_normalize_text(doc, line_len=79, indent=""), file=stream) + print(file=stream) + for optname, optdict, value in options: + help_opt = optdict.get("help") + print(":%s:" % optname, file=stream) + if help_opt: + help_opt = _normalize_text(help_opt, line_len=79, indent=" ") + print(help_opt, file=stream) + if value: + value = str(_format_option_value(optdict, value)) + print(file=stream) + print(" Default: ``%s``" % value.replace("`` ", "```` ``"), file=stream) From 20976c36f1e48061e72379979411bc695564a518 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 18 Dec 2018 23:16:34 +0100 Subject: [PATCH 0029/5147] Refactor - Create a file for the MessageStore class --- pylint/utils/__init__.py | 1 + pylint/utils/message_store.py | 254 ++++++++++++++++++++++++++++++++++ pylint/utils/utils.py | 243 -------------------------------- 3 files changed, 255 insertions(+), 243 deletions(-) create mode 100644 pylint/utils/message_store.py diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index 4abed8c296..0e4d1738f7 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -41,6 +41,7 @@ main pylint class """ +from pylint.utils.message_store import MessagesStore from pylint.utils.utils import ( MSG_STATE_CONFIDENCE, MSG_STATE_SCOPE_CONFIG, diff --git a/pylint/utils/message_store.py b/pylint/utils/message_store.py new file mode 100644 index 0000000000..80c66e483a --- /dev/null +++ b/pylint/utils/message_store.py @@ -0,0 +1,254 @@ +# -*- coding: utf-8 -*- + +# 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 __future__ import print_function + +import collections + +from pylint.exceptions import InvalidMessageError, UnknownMessageError +from pylint.utils.utils import build_message_def + + +class MessagesStore: + """The messages store knows information about every possible message but has + no particular state during analysis. + """ + + def __init__(self): + # Primary registry for all active messages (i.e. all messages + # that can be emitted by pylint for the underlying Python + # version). It contains the 1:1 mapping from symbolic names + # to message definition objects. + # Keys are msg ids, values are a 2-uple with the msg type and the + # msg itself + self._messages_definitions = {} + # Maps alternative names (numeric IDs, deprecated names) to + # message definitions. May contain several names for each definition + # object. + self._alternative_names = {} + self._msgs_by_category = collections.defaultdict(list) + + @property + def messages(self): + """The list of all active messages.""" + return self._messages_definitions.values() + + def add_renamed_message(self, old_id, old_symbol, new_symbol): + """Register the old ID and symbol for a warning that was renamed. + + This allows users to keep using the old ID/symbol in suppressions. + """ + message_definition = self.get_message_definitions(new_symbol)[0] + message_definition.old_names.append((old_id, old_symbol)) + self._register_alternative_name(message_definition, old_id, old_symbol) + + @staticmethod + def get_checker_message_definitions(checker): + """Return the list of messages definitions for a checker. + + :param BaseChecker checker: + :rtype: list + :return: A list of MessageDefinition. + """ + message_definitions = [] + for msgid, msg_tuple in sorted(checker.msgs.items()): + message = build_message_def(checker, msgid, msg_tuple) + message_definitions.append(message) + return message_definitions + + def register_messages_from_checker(self, checker): + """Register all messages from a checker. + + :param BaseChecker checker: + """ + checker_message_definitions = self.get_checker_message_definitions(checker) + self._check_checker_consistency(checker_message_definitions) + for message_definition in checker_message_definitions: + self.register_message(message_definition) + + def register_message(self, message): + """Register a MessageDefinition with consistency in mind. + + :param MessageDefinition message: The message definition being added. + """ + self._check_id_and_symbol_consistency(message.msgid, message.symbol) + self._check_symbol(message.msgid, message.symbol) + self._check_msgid(message.msgid, message.symbol) + for old_name in message.old_names: + self._check_symbol(message.msgid, old_name[1]) + self._messages_definitions[message.symbol] = message + self._register_alternative_name(message, message.msgid, message.symbol) + for old_id, old_symbol in message.old_names: + self._register_alternative_name(message, old_id, old_symbol) + self._msgs_by_category[message.msgid[0]].append(message.msgid) + + @staticmethod + def _check_checker_consistency(messages): + """Check the msgid consistency in a list of messages definitions. + + 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. + + :param list messages: List of MessageDefinition. + :raises InvalidMessageError: If the checker id in the messages are not + always the same + """ + checker_id = None + existing_ids = [] + for message in 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 _register_alternative_name(self, msg, msgid, symbol): + """helper for register_message()""" + self._check_id_and_symbol_consistency(msgid, symbol) + self._alternative_names[msgid] = msg + self._alternative_names[symbol] = msg + + def _check_symbol(self, msgid, symbol): + """Check that a symbol is not already used. """ + other_message = self._messages_definitions.get(symbol) + if other_message: + self._raise_duplicate_msg_id(symbol, msgid, other_message.msgid) + else: + alternative_msgid = None + alternative_message = self._alternative_names.get(symbol) + if alternative_message: + if alternative_message.symbol == symbol: + alternative_msgid = alternative_message.msgid + else: + for old_msgid, old_symbol in alternative_message.old_names: + if old_symbol == symbol: + alternative_msgid = old_msgid + break + if msgid != alternative_msgid: + self._raise_duplicate_msg_id(symbol, msgid, alternative_msgid) + + def _check_msgid(self, msgid, symbol): + for message in self._messages_definitions.values(): + if message.msgid == msgid: + self._raise_duplicate_symbol(msgid, symbol, message.symbol) + + def _check_id_and_symbol_consistency(self, msgid, symbol): + try: + alternative = self._alternative_names[msgid] + except KeyError: + alternative = False + try: + if not alternative: + alternative = self._alternative_names[symbol] + except KeyError: + # There is no alternative names concerning this msgid/symbol. + # So nothing to check + return None + old_symbolic_name = None + old_symbolic_id = None + for alternate_msgid, alternate_symbol in alternative.old_names: + if alternate_msgid == msgid or alternate_symbol == symbol: + old_symbolic_id = alternate_msgid + old_symbolic_name = alternate_symbol + if symbol not in (alternative.symbol, old_symbolic_name): + if msgid == old_symbolic_id: + self._raise_duplicate_symbol(msgid, symbol, old_symbolic_name) + else: + self._raise_duplicate_symbol(msgid, symbol, alternative.symbol) + return None + + @staticmethod + def _raise_duplicate_symbol(msgid, symbol, other_symbol): + """Raise an error when a symbol is duplicated. + + :param str msgid: The msgid corresponding to the symbols + :param str symbol: Offending symbol + :param str other_symbol: Other offending symbol + :raises InvalidMessageError: when a symbol is duplicated. + """ + symbols = [symbol, other_symbol] + symbols.sort() + error_message = "Message id '{msgid}' cannot have both ".format(msgid=msgid) + error_message += "'{other_symbol}' and '{symbol}' as symbolic name.".format( + other_symbol=symbols[0], symbol=symbols[1] + ) + raise InvalidMessageError(error_message) + + @staticmethod + def _raise_duplicate_msg_id(symbol, msgid, other_msgid): + """Raise an error when a msgid is duplicated. + + :param str symbol: The symbol corresponding to the msgids + :param str msgid: Offending msgid + :param str other_msgid: Other offending msgid + :raises InvalidMessageError: when a msgid is duplicated. + """ + msgids = [msgid, other_msgid] + msgids.sort() + error_message = "Message symbol '{symbol}' cannot be used for ".format( + symbol=symbol + ) + error_message += "'{other_msgid}' and '{msgid}' at the same time.".format( + other_msgid=msgids[0], msgid=msgids[1] + ) + raise InvalidMessageError(error_message) + + def get_message_definitions(self, msgid_or_symbol: str) -> list: + """Returns the Message object for this message. + + :param str msgid_or_symbol: msgid_or_symbol may be either a numeric or symbolic id. + :raises UnknownMessageError: if the message id is not defined. + :rtype: List of MessageDefinition + :return: A message definition corresponding to msgid_or_symbol + """ + if msgid_or_symbol[1:].isdigit(): + msgid_or_symbol = msgid_or_symbol.upper() + for source in (self._alternative_names, self._messages_definitions): + try: + return [source[msgid_or_symbol]] + except KeyError: + pass + error_msg = "No such message id or symbol '{msgid_or_symbol}'.".format( + msgid_or_symbol=msgid_or_symbol + ) + raise UnknownMessageError(error_msg) + + def get_msg_display_string(self, msgid): + """Generates a user-consumable representation of a message. + + Can be just the message ID or the ID and the symbol. + """ + message_definitions = self.get_message_definitions(msgid) + if len(message_definitions) == 1: + return repr(message_definitions[0].symbol) + return repr([md.symbol for md in message_definitions]) + + def help_message(self, msgids): + """Display help messages for the given message identifiers""" + for msgid in msgids: + try: + for message_definition in self.get_message_definitions(msgid): + print(message_definition.format_help(checkerref=True)) + print("") + except UnknownMessageError as ex: + print(ex) + print("") + continue + + def list_messages(self): + """Output full messages list documentation in ReST format. """ + messages = sorted(self._messages_definitions.values(), key=lambda m: m.msgid) + for message in messages: + if not message.may_be_emitted(): + continue + print(message.format_help(checkerref=False)) + print("") diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index 4b94567d6d..5c429652f5 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -766,249 +766,6 @@ def get_effective_max_line_number(self): return self._effective_max_line_number -class MessagesStore: - """The messages store knows information about every possible message but has - no particular state during analysis. - """ - - def __init__(self): - # Primary registry for all active messages (i.e. all messages - # that can be emitted by pylint for the underlying Python - # version). It contains the 1:1 mapping from symbolic names - # to message definition objects. - # Keys are msg ids, values are a 2-uple with the msg type and the - # msg itself - self._messages_definitions = {} - # Maps alternative names (numeric IDs, deprecated names) to - # message definitions. May contain several names for each definition - # object. - self._alternative_names = {} - self._msgs_by_category = collections.defaultdict(list) - - @property - def messages(self): - """The list of all active messages.""" - return self._messages_definitions.values() - - def add_renamed_message(self, old_id, old_symbol, new_symbol): - """Register the old ID and symbol for a warning that was renamed. - - This allows users to keep using the old ID/symbol in suppressions. - """ - message_definition = self.get_message_definitions(new_symbol)[0] - message_definition.old_names.append((old_id, old_symbol)) - self._register_alternative_name(message_definition, old_id, old_symbol) - - @staticmethod - def get_checker_message_definitions(checker): - """Return the list of messages definitions for a checker. - - :param BaseChecker checker: - :rtype: list - :return: A list of MessageDefinition. - """ - message_definitions = [] - for msgid, msg_tuple in sorted(checker.msgs.items()): - message = build_message_def(checker, msgid, msg_tuple) - message_definitions.append(message) - return message_definitions - - def register_messages_from_checker(self, checker): - """Register all messages from a checker. - - :param BaseChecker checker: - """ - checker_message_definitions = self.get_checker_message_definitions(checker) - self._check_checker_consistency(checker_message_definitions) - for message_definition in checker_message_definitions: - self.register_message(message_definition) - - def register_message(self, message): - """Register a MessageDefinition with consistency in mind. - - :param MessageDefinition message: The message definition being added. - """ - self._check_id_and_symbol_consistency(message.msgid, message.symbol) - self._check_symbol(message.msgid, message.symbol) - self._check_msgid(message.msgid, message.symbol) - for old_name in message.old_names: - self._check_symbol(message.msgid, old_name[1]) - self._messages_definitions[message.symbol] = message - self._register_alternative_name(message, message.msgid, message.symbol) - for old_id, old_symbol in message.old_names: - self._register_alternative_name(message, old_id, old_symbol) - self._msgs_by_category[message.msgid[0]].append(message.msgid) - - @staticmethod - def _check_checker_consistency(messages): - """Check the msgid consistency in a list of messages definitions. - - 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. - - :param list messages: List of MessageDefinition. - :raises InvalidMessageError: If the checker id in the messages are not - always the same - """ - checker_id = None - existing_ids = [] - for message in 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 _register_alternative_name(self, msg, msgid, symbol): - """helper for register_message()""" - self._check_id_and_symbol_consistency(msgid, symbol) - self._alternative_names[msgid] = msg - self._alternative_names[symbol] = msg - - def _check_symbol(self, msgid, symbol): - """Check that a symbol is not already used. """ - other_message = self._messages_definitions.get(symbol) - if other_message: - self._raise_duplicate_msg_id(symbol, msgid, other_message.msgid) - else: - alternative_msgid = None - alternative_message = self._alternative_names.get(symbol) - if alternative_message: - if alternative_message.symbol == symbol: - alternative_msgid = alternative_message.msgid - else: - for old_msgid, old_symbol in alternative_message.old_names: - if old_symbol == symbol: - alternative_msgid = old_msgid - break - if msgid != alternative_msgid: - self._raise_duplicate_msg_id(symbol, msgid, alternative_msgid) - - def _check_msgid(self, msgid, symbol): - for message in self._messages_definitions.values(): - if message.msgid == msgid: - self._raise_duplicate_symbol(msgid, symbol, message.symbol) - - def _check_id_and_symbol_consistency(self, msgid, symbol): - try: - alternative = self._alternative_names[msgid] - except KeyError: - alternative = False - try: - if not alternative: - alternative = self._alternative_names[symbol] - except KeyError: - # There is no alternative names concerning this msgid/symbol. - # So nothing to check - return None - old_symbolic_name = None - old_symbolic_id = None - for alternate_msgid, alternate_symbol in alternative.old_names: - if alternate_msgid == msgid or alternate_symbol == symbol: - old_symbolic_id = alternate_msgid - old_symbolic_name = alternate_symbol - if symbol not in (alternative.symbol, old_symbolic_name): - if msgid == old_symbolic_id: - self._raise_duplicate_symbol(msgid, symbol, old_symbolic_name) - else: - self._raise_duplicate_symbol(msgid, symbol, alternative.symbol) - return None - - @staticmethod - def _raise_duplicate_symbol(msgid, symbol, other_symbol): - """Raise an error when a symbol is duplicated. - - :param str msgid: The msgid corresponding to the symbols - :param str symbol: Offending symbol - :param str other_symbol: Other offending symbol - :raises InvalidMessageError: when a symbol is duplicated. - """ - symbols = [symbol, other_symbol] - symbols.sort() - error_message = "Message id '{msgid}' cannot have both ".format(msgid=msgid) - error_message += "'{other_symbol}' and '{symbol}' as symbolic name.".format( - other_symbol=symbols[0], symbol=symbols[1] - ) - raise InvalidMessageError(error_message) - - @staticmethod - def _raise_duplicate_msg_id(symbol, msgid, other_msgid): - """Raise an error when a msgid is duplicated. - - :param str symbol: The symbol corresponding to the msgids - :param str msgid: Offending msgid - :param str other_msgid: Other offending msgid - :raises InvalidMessageError: when a msgid is duplicated. - """ - msgids = [msgid, other_msgid] - msgids.sort() - error_message = "Message symbol '{symbol}' cannot be used for ".format( - symbol=symbol - ) - error_message += "'{other_msgid}' and '{msgid}' at the same time.".format( - other_msgid=msgids[0], msgid=msgids[1] - ) - raise InvalidMessageError(error_message) - - def get_message_definitions(self, msgid_or_symbol: str) -> list: - """Returns the Message object for this message. - - :param str msgid_or_symbol: msgid_or_symbol may be either a numeric or symbolic id. - :raises UnknownMessageError: if the message id is not defined. - :rtype: List of MessageDefinition - :return: A message definition corresponding to msgid_or_symbol - """ - if msgid_or_symbol[1:].isdigit(): - msgid_or_symbol = msgid_or_symbol.upper() - for source in (self._alternative_names, self._messages_definitions): - try: - return [source[msgid_or_symbol]] - except KeyError: - pass - error_msg = "No such message id or symbol '{msgid_or_symbol}'.".format( - msgid_or_symbol=msgid_or_symbol - ) - raise UnknownMessageError(error_msg) - - def get_msg_display_string(self, msgid): - """Generates a user-consumable representation of a message. - - Can be just the message ID or the ID and the symbol. - """ - message_definitions = self.get_message_definitions(msgid) - if len(message_definitions) == 1: - return repr(message_definitions[0].symbol) - return repr([md.symbol for md in message_definitions]) - - def help_message(self, msgids): - """Display help messages for the given message identifiers""" - for msgid in msgids: - try: - for message_definition in self.get_message_definitions(msgid): - print(message_definition.format_help(checkerref=True)) - print("") - except UnknownMessageError as ex: - print(ex) - print("") - continue - - def list_messages(self): - """Output full messages list documentation in ReST format. """ - messages = sorted(self._messages_definitions.values(), key=lambda m: m.msgid) - for message in messages: - if not message.may_be_emitted(): - continue - print(message.format_help(checkerref=False)) - print("") - - class ReportsHandlerMixIn: """a mix-in class containing all the reports and stats manipulation related methods for the main lint class From 16e98598c6d8afc826e3cc8f5612618e24d3ec6a Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 9 Dec 2018 14:35:39 +0100 Subject: [PATCH 0030/5147] Refactor - Create a file for each class in utils --- pylint/utils/__init__.py | 22 +- pylint/utils/constants.py | 33 + pylint/utils/file_state.py | 135 ++++ pylint/utils/message.py | 53 ++ pylint/utils/message_definition.py | 77 +++ pylint/utils/message_handler_mix_in.py | 439 +++++++++++++ pylint/utils/normalize_text.py | 15 + pylint/utils/pylint_ast_walker.py | 80 +++ pylint/utils/reports_handler_mix_in.py | 79 +++ pylint/utils/utils.py | 852 +------------------------ pylint/utils/warning_scope.py | 9 + 11 files changed, 944 insertions(+), 850 deletions(-) create mode 100644 pylint/utils/constants.py create mode 100644 pylint/utils/file_state.py create mode 100644 pylint/utils/message.py create mode 100644 pylint/utils/message_definition.py create mode 100644 pylint/utils/message_handler_mix_in.py create mode 100644 pylint/utils/normalize_text.py create mode 100644 pylint/utils/pylint_ast_walker.py create mode 100644 pylint/utils/reports_handler_mix_in.py create mode 100644 pylint/utils/warning_scope.py diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index 0e4d1738f7..d8cfa9d130 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -41,8 +41,7 @@ main pylint class """ -from pylint.utils.message_store import MessagesStore -from pylint.utils.utils import ( +from pylint.utils.constants import ( MSG_STATE_CONFIDENCE, MSG_STATE_SCOPE_CONFIG, MSG_STATE_SCOPE_MODULE, @@ -51,14 +50,16 @@ MSG_TYPES_STATUS, OPTION_RGX, PY_EXTS, - FileState, - Message, - MessageDefinition, - MessagesHandlerMixIn, - MessagesStore, - PyLintASTWalker, - ReportsHandlerMixIn, - WarningScope, +) +from pylint.utils.file_state import FileState +from pylint.utils.message import Message +from pylint.utils.message_definition import MessageDefinition +from pylint.utils.message_handler_mix_in import MessagesHandlerMixIn +from pylint.utils.message_store import MessagesStore +from pylint.utils.normalize_text import normalize_text +from pylint.utils.pylint_ast_walker import PyLintASTWalker +from pylint.utils.reports_handler_mix_in import ReportsHandlerMixIn +from pylint.utils.utils import ( _basename_in_blacklist_re, _check_csv, _format_option_value, @@ -76,3 +77,4 @@ safe_decode, tokenize_module, ) +from pylint.utils.warning_scope import WarningScope diff --git a/pylint/utils/constants.py b/pylint/utils/constants.py new file mode 100644 index 0000000000..4c1a192f7c --- /dev/null +++ b/pylint/utils/constants.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +# 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 + +import re + +MSG_STATE_CONFIDENCE = 2 +_MSG_ORDER = "EWRCIF" +MSG_STATE_SCOPE_CONFIG = 0 +MSG_STATE_SCOPE_MODULE = 1 + +# The line/node distinction does not apply to fatal errors and reports. +_SCOPE_EXEMPT = "FR" + +# Allow stopping after the first semicolon/hash encountered, +# so that an option can be continued with the reasons +# why it is active or disabled. +OPTION_RGX = re.compile(r"\s*#.*\bpylint:\s*([^;#]+)[;#]{0,1}") + +PY_EXTS = (".py", ".pyc", ".pyo", ".pyw", ".so", ".dll") + +MSG_TYPES = { + "I": "info", + "C": "convention", + "R": "refactor", + "W": "warning", + "E": "error", + "F": "fatal", +} +MSG_TYPES_LONG = {v: k for k, v in MSG_TYPES.items()} + +MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1} diff --git a/pylint/utils/file_state.py b/pylint/utils/file_state.py new file mode 100644 index 0000000000..6eefff6ec0 --- /dev/null +++ b/pylint/utils/file_state.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- + +# 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 + +import collections + +from astroid import nodes +from pylint.utils.constants import MSG_STATE_SCOPE_MODULE +from pylint.utils.warning_scope import WarningScope + + +class FileState: + """Hold internal state specific to the currently analyzed file""" + + def __init__(self, modname=None): + self.base_name = modname + self._module_msgs_state = {} + self._raw_module_msgs_state = {} + self._ignored_msgs = collections.defaultdict(set) + self._suppression_mapping = {} + self._effective_max_line_number = None + + def collect_block_lines(self, msgs_store, module_node): + """Walk the AST to collect block level options line numbers.""" + for msg, lines in self._module_msgs_state.items(): + self._raw_module_msgs_state[msg] = lines.copy() + orig_state = self._module_msgs_state.copy() + self._module_msgs_state = {} + self._suppression_mapping = {} + self._effective_max_line_number = module_node.tolineno + self._collect_block_lines(msgs_store, module_node, orig_state) + + def _collect_block_lines(self, msgs_store, node, msg_state): + """Recursively walk (depth first) AST to collect block level options + line numbers. + """ + for child in node.get_children(): + self._collect_block_lines(msgs_store, child, msg_state) + first = node.fromlineno + last = node.tolineno + # first child line number used to distinguish between disable + # which are the first child of scoped node with those defined later. + # For instance in the code below: + # + # 1. def meth8(self): + # 2. """test late disabling""" + # 3. # pylint: disable=E1102 + # 4. print self.blip + # 5. # pylint: disable=E1101 + # 6. print self.bla + # + # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6 + # + # this is necessary to disable locally messages applying to class / + # function using their fromlineno + if ( + isinstance(node, (nodes.Module, nodes.ClassDef, nodes.FunctionDef)) + and node.body + ): + firstchildlineno = node.body[0].fromlineno + else: + firstchildlineno = last + for msgid, lines in msg_state.items(): + for lineno, state in list(lines.items()): + original_lineno = lineno + if first > lineno or last < lineno: + continue + # Set state for all lines for this block, if the + # warning is applied to nodes. + message_definitions = msgs_store.get_message_definitions(msgid) + for message_definition in message_definitions: + if message_definition.scope == WarningScope.NODE: + if lineno > firstchildlineno: + state = True + first_, last_ = node.block_range(lineno) + else: + first_ = lineno + last_ = last + for line in range(first_, last_ + 1): + # do not override existing entries + if line in self._module_msgs_state.get(msgid, ()): + continue + if line in lines: # state change in the same block + state = lines[line] + original_lineno = line + if not state: + self._suppression_mapping[(msgid, line)] = original_lineno + try: + self._module_msgs_state[msgid][line] = state + except KeyError: + self._module_msgs_state[msgid] = {line: state} + del lines[lineno] + + def set_msg_status(self, msg, line, status): + """Set status (enabled/disable) for a given message at a given line""" + assert line > 0 + try: + self._module_msgs_state[msg.msgid][line] = status + except KeyError: + self._module_msgs_state[msg.msgid] = {line: status} + + def handle_ignored_message( + self, state_scope, msgid, line, node, args, confidence + ): # pylint: disable=unused-argument + """Report an ignored message. + + state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG, + depending on whether the message was disabled locally in the module, + or globally. The other arguments are the same as for add_message. + """ + if state_scope == MSG_STATE_SCOPE_MODULE: + try: + orig_line = self._suppression_mapping[(msgid, line)] + self._ignored_msgs[(msgid, orig_line)].add(line) + except KeyError: + pass + + def iter_spurious_suppression_messages(self, msgs_store): + for warning, lines in self._raw_module_msgs_state.items(): + for line, enable in lines.items(): + if not enable and (warning, line) not in self._ignored_msgs: + yield "useless-suppression", line, ( + msgs_store.get_msg_display_string(warning), + ) + # don't use iteritems here, _ignored_msgs may be modified by add_message + for (warning, from_), lines in list(self._ignored_msgs.items()): + for line in lines: + yield "suppressed-message", line, ( + msgs_store.get_msg_display_string(warning), + from_, + ) + + def get_effective_max_line_number(self): + return self._effective_max_line_number diff --git a/pylint/utils/message.py b/pylint/utils/message.py new file mode 100644 index 0000000000..ea1b9ae7ad --- /dev/null +++ b/pylint/utils/message.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# 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 + + +import collections + +from pylint.utils.constants import MSG_TYPES + +_MsgBase = collections.namedtuple( + "_MsgBase", + [ + "msg_id", + "symbol", + "msg", + "C", + "category", + "confidence", + "abspath", + "path", + "module", + "obj", + "line", + "column", + ], +) + + +class Message(_MsgBase): + """This class represent a message to be issued by the reporters""" + + def __new__(cls, msg_id, symbol, location, msg, confidence): + return _MsgBase.__new__( + cls, + msg_id, + symbol, + msg, + msg_id[0], + MSG_TYPES[msg_id[0]], + confidence, + *location + ) + + def format(self, template): + """Format the message according to the given template. + + The template format is the one of the format method : + cf. http://docs.python.org/2/library/string.html#formatstrings + """ + # For some reason, _asdict on derived namedtuples does not work with + # Python 3.4. Needs some investigation. + return template.format(**dict(zip(self._fields, self))) diff --git a/pylint/utils/message_definition.py b/pylint/utils/message_definition.py new file mode 100644 index 0000000000..6f8c32fc38 --- /dev/null +++ b/pylint/utils/message_definition.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +# 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 + +import sys + +from pylint.exceptions import InvalidMessageError +from pylint.utils.constants import MSG_TYPES +from pylint.utils.normalize_text import normalize_text + + +class MessageDefinition: + def __init__( + self, + checker, + msgid, + msg, + descr, + symbol, + scope, + minversion=None, + maxversion=None, + old_names=None, + ): + self.checker = checker + if len(msgid) != 5: + raise InvalidMessageError("Invalid message id %r" % msgid) + if not msgid[0] in MSG_TYPES: + raise InvalidMessageError("Bad message type %s in %r" % (msgid[0], msgid)) + self.msgid = msgid + self.msg = msg + self.descr = descr + self.symbol = symbol + self.scope = scope + self.minversion = minversion + self.maxversion = maxversion + self.old_names = old_names or [] + + def __repr__(self): + return "MessageDefinition:{}".format(self.__dict__) + + def may_be_emitted(self): + """return True if message may be emitted using the current interpreter""" + if self.minversion is not None and self.minversion > sys.version_info: + return False + if self.maxversion is not None and self.maxversion <= sys.version_info: + return False + return True + + def format_help(self, checkerref=False): + """return the help string for the given message id""" + desc = self.descr + if checkerref: + desc += " This message belongs to the %s checker." % self.checker.name + title = self.msg + if self.symbol: + msgid = "%s (%s)" % (self.symbol, self.msgid) + else: + msgid = self.msgid + if self.minversion or self.maxversion: + restr = [] + if self.minversion: + restr.append("< %s" % ".".join([str(n) for n in self.minversion])) + if self.maxversion: + restr.append(">= %s" % ".".join([str(n) for n in self.maxversion])) + restr = " or ".join(restr) + if checkerref: + desc += " It can't be emitted when using Python %s." % restr + else: + desc += " This message can't be emitted when using Python %s." % restr + desc = normalize_text(" ".join(desc.split()), indent=" ") + if title != "%s": + title = title.splitlines()[0] + + return ":%s: *%s*\n%s" % (msgid, title.rstrip(" "), desc) + return ":%s:\n%s" % (msgid, desc) diff --git a/pylint/utils/message_handler_mix_in.py b/pylint/utils/message_handler_mix_in.py new file mode 100644 index 0000000000..4b817db81a --- /dev/null +++ b/pylint/utils/message_handler_mix_in.py @@ -0,0 +1,439 @@ +# -*- coding: utf-8 -*- + +# 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 __future__ import print_function + +import sys +from inspect import cleandoc + +from pylint.exceptions import InvalidMessageError, UnknownMessageError +from pylint.interfaces import UNDEFINED +from pylint.utils.constants import ( + _MSG_ORDER, + _SCOPE_EXEMPT, + MSG_STATE_CONFIDENCE, + MSG_STATE_SCOPE_CONFIG, + MSG_STATE_SCOPE_MODULE, + MSG_TYPES, + MSG_TYPES_STATUS, +) +from pylint.utils.message import Message +from pylint.utils.utils import ( + WarningScope, + _rest_format_section, + build_message_def, + category_id, + get_module_and_frameid, +) + + +class MessagesHandlerMixIn: + """a mix-in class containing all the messages related methods for the main + lint class + """ + + __by_id_managed_msgs = [] # type: ignore + + def __init__(self): + self._msgs_state = {} + self.msg_status = 0 + + def _checker_messages(self, checker): + for known_checker in self._checkers[checker.lower()]: + for msgid in known_checker.msgs: + yield msgid + + @classmethod + def clear_by_id_managed_msgs(cls): + cls.__by_id_managed_msgs.clear() + + @classmethod + def get_by_id_managed_msgs(cls): + return cls.__by_id_managed_msgs + + def _register_by_id_managed_msg(self, msgid, line, is_disabled=True): + """If the msgid is a numeric one, then register it to inform the user + it could furnish instead a symbolic msgid.""" + try: + message_definitions = self.msgs_store.get_message_definitions(msgid) + for message_definition in message_definitions: + if msgid == message_definition.msgid: + MessagesHandlerMixIn.__by_id_managed_msgs.append( + ( + self.current_name, + message_definition.msgid, + message_definition.symbol, + line, + is_disabled, + ) + ) + except UnknownMessageError: + pass + + def disable(self, msgid, scope="package", line=None, ignore_unknown=False): + """don't output message of the given id""" + self._set_msg_status( + msgid, enable=False, scope=scope, line=line, ignore_unknown=ignore_unknown + ) + self._register_by_id_managed_msg(msgid, line) + + def enable(self, msgid, scope="package", line=None, ignore_unknown=False): + """reenable message of the given id""" + self._set_msg_status( + msgid, enable=True, scope=scope, line=line, ignore_unknown=ignore_unknown + ) + self._register_by_id_managed_msg(msgid, line, is_disabled=False) + + def _set_msg_status( + self, msgid, enable, scope="package", line=None, ignore_unknown=False + ): + assert scope in ("package", "module") + + if msgid == "all": + for _msgid in MSG_TYPES: + self._set_msg_status(_msgid, enable, scope, line, ignore_unknown) + if enable and not self._python3_porting_mode: + # Don't activate the python 3 porting checker if it wasn't activated explicitly. + self.disable("python3") + return + + # msgid is a category? + catid = category_id(msgid) + if catid is not None: + for _msgid in self.msgs_store._msgs_by_category.get(catid): + self._set_msg_status(_msgid, enable, scope, line) + return + + # msgid is a checker name? + if msgid.lower() in self._checkers: + msgs_store = self.msgs_store + for checker in self._checkers[msgid.lower()]: + for _msgid in checker.msgs: + if _msgid in msgs_store._alternative_names: + self._set_msg_status(_msgid, enable, scope, line) + return + + # msgid is report id? + if msgid.lower().startswith("rp"): + if enable: + self.enable_report(msgid) + else: + self.disable_report(msgid) + return + + try: + # msgid is a symbolic or numeric msgid. + message_definitions = self.msgs_store.get_message_definitions(msgid) + except UnknownMessageError: + if ignore_unknown: + return + raise + for message_definition in message_definitions: + self._set_one_msg_status(scope, message_definition, line, enable) + + def _set_one_msg_status(self, scope, msg, line, enable): + if scope == "module": + self.file_state.set_msg_status(msg, line, enable) + if not enable and msg.symbol != "locally-disabled": + self.add_message( + "locally-disabled", line=line, args=(msg.symbol, msg.msgid) + ) + else: + msgs = self._msgs_state + msgs[msg.msgid] = enable + # sync configuration object + self.config.enable = [ + self._message_symbol(mid) for mid, val in sorted(msgs.items()) if val + ] + self.config.disable = [ + self._message_symbol(mid) + for mid, val in sorted(msgs.items()) + if not val + ] + + def _message_symbol(self, msgid): + """Get the message symbol of the given message id + + Return the original message id if the message does not + exist. + """ + try: + return [md.symbol for md in self.msgs_store.get_message_definitions(msgid)] + except UnknownMessageError: + return msgid + + def get_message_state_scope(self, msgid, line=None, confidence=UNDEFINED): + """Returns the scope at which a message was enabled/disabled.""" + if self.config.confidence and confidence.name not in self.config.confidence: + return MSG_STATE_CONFIDENCE + try: + if line in self.file_state._module_msgs_state[msgid]: + return MSG_STATE_SCOPE_MODULE + except (KeyError, TypeError): + return MSG_STATE_SCOPE_CONFIG + return None + + def is_message_enabled(self, msg_descr, line=None, confidence=None): + """return true if the message associated to the given message id is + enabled + + msgid may be either a numeric or symbolic message id. + """ + if self.config.confidence and confidence: + if confidence.name not in self.config.confidence: + return False + try: + message_definitions = self.msgs_store.get_message_definitions(msg_descr) + msgids = [md.msgid for md in message_definitions] + except UnknownMessageError: + # The linter checks for messages that are not registered + # due to version mismatch, just treat them as message IDs + # for now. + msgids = [msg_descr] + for msgid in msgids: + if self.is_one_message_enabled(msgid, line): + return True + return False + + def is_one_message_enabled(self, msgid, line): + if line is None: + return self._msgs_state.get(msgid, True) + try: + return self.file_state._module_msgs_state[msgid][line] + except KeyError: + # Check if the message's line is after the maximum line existing in ast tree. + # This line won't appear in the ast tree and won't be referred in + #  self.file_state._module_msgs_state + # This happens for example with a commented line at the end of a module. + max_line_number = self.file_state.get_effective_max_line_number() + if max_line_number and line > max_line_number: + fallback = True + lines = self.file_state._raw_module_msgs_state.get(msgid, {}) + + # Doesn't consider scopes, as a disable can be in a different scope + # than that of the current line. + closest_lines = reversed( + [ + (message_line, enable) + for message_line, enable in lines.items() + if message_line <= line + ] + ) + last_line, is_enabled = next(closest_lines, (None, None)) + if last_line is not None: + fallback = is_enabled + + return self._msgs_state.get(msgid, fallback) + return self._msgs_state.get(msgid, True) + + def add_message( + self, + msg_descr, + line=None, + node=None, + args=None, + confidence=UNDEFINED, + col_offset=None, + ): + """Adds a message given by ID or name. + + If provided, the message string is expanded using args. + + AST checkers must provide the node argument (but may optionally + provide line if the line number is different), raw and token checkers + must provide the line argument. + """ + message_definitions = self.msgs_store.get_message_definitions(msg_descr) + for message_definition in message_definitions: + self.add_one_message( + message_definition, line, node, args, confidence, col_offset + ) + + def add_one_message( + self, message_definition, line, node, args, confidence, col_offset + ): + msgid = message_definition.msgid + # backward compatibility, message may not have a symbol + symbol = message_definition.symbol or msgid + # Fatal messages and reports are special, the node/scope distinction + # does not apply to them. + if msgid[0] not in _SCOPE_EXEMPT: + if message_definition.scope == WarningScope.LINE: + if line is None: + raise InvalidMessageError( + "Message %s must provide line, got None" % msgid + ) + if node is not None: + raise InvalidMessageError( + "Message %s must only provide line, " + "got line=%s, node=%s" % (msgid, line, node) + ) + elif message_definition.scope == WarningScope.NODE: + # Node-based warnings may provide an override line. + if node is None: + raise InvalidMessageError( + "Message %s must provide Node, got None" % msgid + ) + + if line is None and node is not None: + line = node.fromlineno + if col_offset is None and hasattr(node, "col_offset"): + col_offset = ( + node.col_offset + ) # XXX measured in bytes for utf-8, divide by two for chars? + + # should this message be displayed + if not self.is_message_enabled(msgid, line, confidence): + self.file_state.handle_ignored_message( + self.get_message_state_scope(msgid, line, confidence), + msgid, + line, + node, + args, + confidence, + ) + return + # update stats + msg_cat = MSG_TYPES[msgid[0]] + self.msg_status |= MSG_TYPES_STATUS[msgid[0]] + self.stats[msg_cat] += 1 + self.stats["by_module"][self.current_name][msg_cat] += 1 + try: + self.stats["by_msg"][symbol] += 1 + except KeyError: + self.stats["by_msg"][symbol] = 1 + # expand message ? + msg = message_definition.msg + if args: + msg %= args + # get module and object + if node is None: + module, obj = self.current_name, "" + abspath = self.current_file + else: + module, obj = get_module_and_frameid(node) + abspath = node.root().file + path = abspath.replace(self.reporter.path_strip_prefix, "", 1) + # add the message + self.reporter.handle_message( + Message( + msgid, + symbol, + (abspath, path, module, obj, line or 1, col_offset or 0), + msg, + confidence, + ) + ) + + def print_full_documentation(self, stream=None): + """output a full documentation in ReST format""" + if not stream: + stream = sys.stdout + + print("Pylint global options and switches", file=stream) + print("----------------------------------", file=stream) + print("", file=stream) + print("Pylint provides global options and switches.", file=stream) + print("", file=stream) + + by_checker = {} + for checker in self.get_checkers(): + if checker.name == "master": + if checker.options: + for section, options in checker.options_by_section(): + if section is None: + title = "General options" + else: + title = "%s options" % section.capitalize() + print(title, file=stream) + print("~" * len(title), file=stream) + _rest_format_section(stream, None, options) + print("", file=stream) + else: + name = checker.name + try: + by_checker[name]["options"] += checker.options_and_values() + by_checker[name]["msgs"].update(checker.msgs) + by_checker[name]["reports"] += checker.reports + except KeyError: + by_checker[name] = { + "options": list(checker.options_and_values()), + "msgs": dict(checker.msgs), + "reports": list(checker.reports), + } + + print("Pylint checkers' options and switches", file=stream) + print("-------------------------------------", file=stream) + print("", file=stream) + print("Pylint checkers can provide three set of features:", file=stream) + print("", file=stream) + print("* options that control their execution,", file=stream) + print("* messages that they can raise,", file=stream) + print("* reports that they can generate.", file=stream) + print("", file=stream) + print("Below is a list of all checkers and their features.", file=stream) + print("", file=stream) + + for checker, info in sorted(by_checker.items()): + self._print_checker_doc(checker, info, stream=stream) + + @staticmethod + def _print_checker_doc(checker_name, info, stream=None): + """Helper method for print_full_documentation. + + Also used by doc/exts/pylint_extensions.py. + """ + if not stream: + stream = sys.stdout + + doc = info.get("doc") + module = info.get("module") + msgs = info.get("msgs") + options = info.get("options") + reports = info.get("reports") + + checker_title = "%s checker" % (checker_name.replace("_", " ").title()) + + if module: + # Provide anchor to link against + print(".. _%s:\n" % module, file=stream) + print(checker_title, file=stream) + print("~" * len(checker_title), file=stream) + print("", file=stream) + if module: + print("This checker is provided by ``%s``." % module, file=stream) + print("Verbatim name of the checker is ``%s``." % checker_name, file=stream) + print("", file=stream) + if doc: + # Provide anchor to link against + title = "{} Documentation".format(checker_title) + print(title, file=stream) + print("^" * len(title), file=stream) + print(cleandoc(doc), file=stream) + print("", file=stream) + if options: + title = "{} Options".format(checker_title) + print(title, file=stream) + print("^" * len(title), file=stream) + _rest_format_section(stream, None, options) + print("", file=stream) + if msgs: + title = "{} Messages".format(checker_title) + print(title, file=stream) + print("^" * len(title), file=stream) + for msgid, msg in sorted( + msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) + ): + msg = build_message_def(checker_name, msgid, msg) + print(msg.format_help(checkerref=False), file=stream) + print("", file=stream) + if reports: + title = "{} Reports".format(checker_title) + print(title, file=stream) + print("^" * len(title), file=stream) + for report in reports: + print(":%s: %s" % report[:2], file=stream) + print("", file=stream) + print("", file=stream) diff --git a/pylint/utils/normalize_text.py b/pylint/utils/normalize_text.py new file mode 100644 index 0000000000..787f03d00e --- /dev/null +++ b/pylint/utils/normalize_text.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +# 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 + +import textwrap + + +def normalize_text(text, line_len=80, indent=""): + """Wrap the text on the given line length.""" + return "\n".join( + textwrap.wrap( + text, width=line_len, initial_indent=indent, subsequent_indent=indent + ) + ) diff --git a/pylint/utils/pylint_ast_walker.py b/pylint/utils/pylint_ast_walker.py new file mode 100644 index 0000000000..e758422a61 --- /dev/null +++ b/pylint/utils/pylint_ast_walker.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +# 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 + +import collections + +from astroid import nodes + + +class PyLintASTWalker: + def __init__(self, linter): + # callbacks per node types + self.nbstatements = 0 + self.visit_events = collections.defaultdict(list) + self.leave_events = collections.defaultdict(list) + self.linter = linter + + def _is_method_enabled(self, method): + if not hasattr(method, "checks_msgs"): + return True + for msg_desc in method.checks_msgs: + if self.linter.is_message_enabled(msg_desc): + return True + return False + + def add_checker(self, checker): + """walk to the checker's dir and collect visit and leave methods""" + # XXX : should be possible to merge needed_checkers and add_checker + vcids = set() + lcids = set() + visits = self.visit_events + leaves = self.leave_events + for member in dir(checker): + cid = member[6:] + if cid == "default": + continue + if member.startswith("visit_"): + v_meth = getattr(checker, member) + # don't use visit_methods with no activated message: + if self._is_method_enabled(v_meth): + visits[cid].append(v_meth) + vcids.add(cid) + elif member.startswith("leave_"): + l_meth = getattr(checker, member) + # don't use leave_methods with no activated message: + if self._is_method_enabled(l_meth): + leaves[cid].append(l_meth) + lcids.add(cid) + visit_default = getattr(checker, "visit_default", None) + if visit_default: + for cls in nodes.ALL_NODE_CLASSES: + cid = cls.__name__.lower() + if cid not in vcids: + visits[cid].append(visit_default) + # for now we have no "leave_default" method in Pylint + + def walk(self, astroid): + """call visit events of astroid checkers for the given node, recurse on + its children, then leave events. + """ + cid = astroid.__class__.__name__.lower() + + # Detect if the node is a new name for a deprecated alias. + # In this case, favour the methods for the deprecated + # alias if any, in order to maintain backwards + # compatibility. + visit_events = self.visit_events.get(cid, ()) + leave_events = self.leave_events.get(cid, ()) + + if astroid.is_statement: + self.nbstatements += 1 + # generate events for this node on each checker + for cb in visit_events or (): + cb(astroid) + # recurse on children + for child in astroid.get_children(): + self.walk(child) + for cb in leave_events or (): + cb(astroid) diff --git a/pylint/utils/reports_handler_mix_in.py b/pylint/utils/reports_handler_mix_in.py new file mode 100644 index 0000000000..6f91a9784a --- /dev/null +++ b/pylint/utils/reports_handler_mix_in.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +# 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 + +import collections + +from pylint.exceptions import EmptyReportError +from pylint.reporters.ureports.nodes import Section + + +class ReportsHandlerMixIn: + """a mix-in class containing all the reports and stats manipulation + related methods for the main lint class + """ + + def __init__(self): + self._reports = collections.defaultdict(list) + self._reports_state = {} + + def report_order(self): + """ Return a list of reports, sorted in the order + in which they must be called. + """ + return list(self._reports) + + def register_report(self, reportid, r_title, r_cb, checker): + """register a report + + reportid is the unique identifier for the report + r_title the report's title + r_cb the method to call to make the report + checker is the checker defining the report + """ + reportid = reportid.upper() + self._reports[checker].append((reportid, r_title, r_cb)) + + def enable_report(self, reportid): + """disable the report of the given id""" + reportid = reportid.upper() + self._reports_state[reportid] = True + + def disable_report(self, reportid): + """disable the report of the given id""" + reportid = reportid.upper() + self._reports_state[reportid] = False + + def report_is_enabled(self, reportid): + """return true if the report associated to the given identifier is + enabled + """ + return self._reports_state.get(reportid, True) + + def make_reports(self, stats, old_stats): + """render registered reports""" + sect = Section("Report", "%s statements analysed." % (self.stats["statement"])) + for checker in self.report_order(): + for reportid, r_title, r_cb in self._reports[checker]: + if not self.report_is_enabled(reportid): + continue + report_sect = Section(r_title) + try: + r_cb(report_sect, stats, old_stats) + except EmptyReportError: + continue + report_sect.report_id = reportid + sect.append(report_sect) + return sect + + def add_stats(self, **kwargs): + """add some stats entries to the statistic dictionary + raise an AssertionError if there is a key conflict + """ + for key, value in kwargs.items(): + if key[-1] == "_": + key = key[:-1] + assert key not in self.stats + self.stats[key] = value + return self.stats diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index 5c429652f5..6c6cd269bb 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- +# 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 __future__ import print_function import codecs @@ -7,91 +10,16 @@ import os import re import sys -import textwrap import tokenize import warnings -from inspect import cleandoc from os.path import basename, dirname, exists, isdir, join, normpath, splitext -from astroid import Module, modutils, nodes -from pylint.exceptions import EmptyReportError, InvalidMessageError, UnknownMessageError -from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements -from pylint.reporters.ureports.nodes import Section - -MSG_TYPES = { - "I": "info", - "C": "convention", - "R": "refactor", - "W": "warning", - "E": "error", - "F": "fatal", -} -MSG_TYPES_LONG = {v: k for k, v in MSG_TYPES.items()} - -MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1} - -_MSG_ORDER = "EWRCIF" -MSG_STATE_SCOPE_CONFIG = 0 -MSG_STATE_SCOPE_MODULE = 1 -MSG_STATE_CONFIDENCE = 2 - -# Allow stopping after the first semicolon/hash encountered, -# so that an option can be continued with the reasons -# why it is active or disabled. -OPTION_RGX = re.compile(r"\s*#.*\bpylint:\s*([^;#]+)[;#]{0,1}") - -# The line/node distinction does not apply to fatal errors and reports. -_SCOPE_EXEMPT = "FR" - - -class WarningScope: - LINE = "line-based-msg" - NODE = "node-based-msg" - - -_MsgBase = collections.namedtuple( - "_MsgBase", - [ - "msg_id", - "symbol", - "msg", - "C", - "category", - "confidence", - "abspath", - "path", - "module", - "obj", - "line", - "column", - ], -) - - -class Message(_MsgBase): - """This class represent a message to be issued by the reporters""" - - def __new__(cls, msg_id, symbol, location, msg, confidence): - return _MsgBase.__new__( - cls, - msg_id, - symbol, - msg, - msg_id[0], - MSG_TYPES[msg_id[0]], - confidence, - *location - ) - - def format(self, template): - """Format the message according to the given template. - - The template format is the one of the format method : - cf. http://docs.python.org/2/library/string.html#formatstrings - """ - # For some reason, _asdict on derived namedtuples does not work with - # Python 3.4. Needs some investigation. - return template.format(**dict(zip(self._fields, self))) +from astroid import Module, modutils +from pylint.interfaces import IRawChecker, ITokenChecker, implements +from pylint.utils.constants import MSG_TYPES, MSG_TYPES_LONG, PY_EXTS +from pylint.utils.message_definition import MessageDefinition +from pylint.utils.normalize_text import normalize_text +from pylint.utils.warning_scope import WarningScope def get_module_and_frameid(node): @@ -164,678 +92,6 @@ def build_message_def(checker, msgid, msg_tuple): return MessageDefinition(checker, msgid, msg, descr, symbol, **options) -class MessageDefinition: - def __init__( - self, - checker, - msgid, - msg, - descr, - symbol, - scope, - minversion=None, - maxversion=None, - old_names=None, - ): - self.checker = checker - if len(msgid) != 5: - raise InvalidMessageError("Invalid message id %r" % msgid) - if not msgid[0] in MSG_TYPES: - raise InvalidMessageError("Bad message type %s in %r" % (msgid[0], msgid)) - self.msgid = msgid - self.msg = msg - self.descr = descr - self.symbol = symbol - self.scope = scope - self.minversion = minversion - self.maxversion = maxversion - self.old_names = old_names or [] - - def __repr__(self): - return "MessageDefinition:{}".format(self.__dict__) - - def may_be_emitted(self): - """return True if message may be emitted using the current interpreter""" - if self.minversion is not None and self.minversion > sys.version_info: - return False - if self.maxversion is not None and self.maxversion <= sys.version_info: - return False - return True - - def format_help(self, checkerref=False): - """return the help string for the given message id""" - desc = self.descr - if checkerref: - desc += " This message belongs to the %s checker." % self.checker.name - title = self.msg - if self.symbol: - msgid = "%s (%s)" % (self.symbol, self.msgid) - else: - msgid = self.msgid - if self.minversion or self.maxversion: - restr = [] - if self.minversion: - restr.append("< %s" % ".".join([str(n) for n in self.minversion])) - if self.maxversion: - restr.append(">= %s" % ".".join([str(n) for n in self.maxversion])) - restr = " or ".join(restr) - if checkerref: - desc += " It can't be emitted when using Python %s." % restr - else: - desc += " This message can't be emitted when using Python %s." % restr - desc = _normalize_text(" ".join(desc.split()), indent=" ") - if title != "%s": - title = title.splitlines()[0] - - return ":%s: *%s*\n%s" % (msgid, title.rstrip(" "), desc) - return ":%s:\n%s" % (msgid, desc) - - -class MessagesHandlerMixIn: - """a mix-in class containing all the messages related methods for the main - lint class - """ - - __by_id_managed_msgs = [] # type: ignore - - def __init__(self): - self._msgs_state = {} - self.msg_status = 0 - - def _checker_messages(self, checker): - for known_checker in self._checkers[checker.lower()]: - for msgid in known_checker.msgs: - yield msgid - - @classmethod - def clear_by_id_managed_msgs(cls): - cls.__by_id_managed_msgs.clear() - - @classmethod - def get_by_id_managed_msgs(cls): - return cls.__by_id_managed_msgs - - def _register_by_id_managed_msg(self, msgid, line, is_disabled=True): - """If the msgid is a numeric one, then register it to inform the user - it could furnish instead a symbolic msgid.""" - try: - message_definitions = self.msgs_store.get_message_definitions(msgid) - for message_definition in message_definitions: - if msgid == message_definition.msgid: - MessagesHandlerMixIn.__by_id_managed_msgs.append( - ( - self.current_name, - message_definition.msgid, - message_definition.symbol, - line, - is_disabled, - ) - ) - except UnknownMessageError: - pass - - def disable(self, msgid, scope="package", line=None, ignore_unknown=False): - """don't output message of the given id""" - self._set_msg_status( - msgid, enable=False, scope=scope, line=line, ignore_unknown=ignore_unknown - ) - self._register_by_id_managed_msg(msgid, line) - - def enable(self, msgid, scope="package", line=None, ignore_unknown=False): - """reenable message of the given id""" - self._set_msg_status( - msgid, enable=True, scope=scope, line=line, ignore_unknown=ignore_unknown - ) - self._register_by_id_managed_msg(msgid, line, is_disabled=False) - - def _set_msg_status( - self, msgid, enable, scope="package", line=None, ignore_unknown=False - ): - assert scope in ("package", "module") - - if msgid == "all": - for _msgid in MSG_TYPES: - self._set_msg_status(_msgid, enable, scope, line, ignore_unknown) - if enable and not self._python3_porting_mode: - # Don't activate the python 3 porting checker if it wasn't activated explicitly. - self.disable("python3") - return - - # msgid is a category? - catid = category_id(msgid) - if catid is not None: - for _msgid in self.msgs_store._msgs_by_category.get(catid): - self._set_msg_status(_msgid, enable, scope, line) - return - - # msgid is a checker name? - if msgid.lower() in self._checkers: - msgs_store = self.msgs_store - for checker in self._checkers[msgid.lower()]: - for _msgid in checker.msgs: - if _msgid in msgs_store._alternative_names: - self._set_msg_status(_msgid, enable, scope, line) - return - - # msgid is report id? - if msgid.lower().startswith("rp"): - if enable: - self.enable_report(msgid) - else: - self.disable_report(msgid) - return - - try: - # msgid is a symbolic or numeric msgid. - message_definitions = self.msgs_store.get_message_definitions(msgid) - except UnknownMessageError: - if ignore_unknown: - return - raise - for message_definition in message_definitions: - self._set_one_msg_status(scope, message_definition, line, enable) - - def _set_one_msg_status(self, scope, msg, line, enable): - if scope == "module": - self.file_state.set_msg_status(msg, line, enable) - if not enable and msg.symbol != "locally-disabled": - self.add_message( - "locally-disabled", line=line, args=(msg.symbol, msg.msgid) - ) - else: - msgs = self._msgs_state - msgs[msg.msgid] = enable - # sync configuration object - self.config.enable = [ - self._message_symbol(mid) for mid, val in sorted(msgs.items()) if val - ] - self.config.disable = [ - self._message_symbol(mid) - for mid, val in sorted(msgs.items()) - if not val - ] - - def _message_symbol(self, msgid): - """Get the message symbol of the given message id - - Return the original message id if the message does not - exist. - """ - try: - return [md.symbol for md in self.msgs_store.get_message_definitions(msgid)] - except UnknownMessageError: - return msgid - - def get_message_state_scope(self, msgid, line=None, confidence=UNDEFINED): - """Returns the scope at which a message was enabled/disabled.""" - if self.config.confidence and confidence.name not in self.config.confidence: - return MSG_STATE_CONFIDENCE - try: - if line in self.file_state._module_msgs_state[msgid]: - return MSG_STATE_SCOPE_MODULE - except (KeyError, TypeError): - return MSG_STATE_SCOPE_CONFIG - return None - - def is_message_enabled(self, msg_descr, line=None, confidence=None): - """return true if the message associated to the given message id is - enabled - - msgid may be either a numeric or symbolic message id. - """ - if self.config.confidence and confidence: - if confidence.name not in self.config.confidence: - return False - try: - message_definitions = self.msgs_store.get_message_definitions(msg_descr) - msgids = [md.msgid for md in message_definitions] - except UnknownMessageError: - # The linter checks for messages that are not registered - # due to version mismatch, just treat them as message IDs - # for now. - msgids = [msg_descr] - for msgid in msgids: - if self.is_one_message_enabled(msgid, line): - return True - return False - - def is_one_message_enabled(self, msgid, line): - if line is None: - return self._msgs_state.get(msgid, True) - try: - return self.file_state._module_msgs_state[msgid][line] - except KeyError: - # Check if the message's line is after the maximum line existing in ast tree. - # This line won't appear in the ast tree and won't be referred in - #  self.file_state._module_msgs_state - # This happens for example with a commented line at the end of a module. - max_line_number = self.file_state.get_effective_max_line_number() - if max_line_number and line > max_line_number: - fallback = True - lines = self.file_state._raw_module_msgs_state.get(msgid, {}) - - # Doesn't consider scopes, as a disable can be in a different scope - # than that of the current line. - closest_lines = reversed( - [ - (message_line, enable) - for message_line, enable in lines.items() - if message_line <= line - ] - ) - last_line, is_enabled = next(closest_lines, (None, None)) - if last_line is not None: - fallback = is_enabled - - return self._msgs_state.get(msgid, fallback) - return self._msgs_state.get(msgid, True) - - def add_message( - self, - msg_descr, - line=None, - node=None, - args=None, - confidence=UNDEFINED, - col_offset=None, - ): - """Adds a message given by ID or name. - - If provided, the message string is expanded using args. - - AST checkers must provide the node argument (but may optionally - provide line if the line number is different), raw and token checkers - must provide the line argument. - """ - message_definitions = self.msgs_store.get_message_definitions(msg_descr) - for message_definition in message_definitions: - self.add_one_message( - message_definition, line, node, args, confidence, col_offset - ) - - def add_one_message( - self, message_definition, line, node, args, confidence, col_offset - ): - msgid = message_definition.msgid - # backward compatibility, message may not have a symbol - symbol = message_definition.symbol or msgid - # Fatal messages and reports are special, the node/scope distinction - # does not apply to them. - if msgid[0] not in _SCOPE_EXEMPT: - if message_definition.scope == WarningScope.LINE: - if line is None: - raise InvalidMessageError( - "Message %s must provide line, got None" % msgid - ) - if node is not None: - raise InvalidMessageError( - "Message %s must only provide line, " - "got line=%s, node=%s" % (msgid, line, node) - ) - elif message_definition.scope == WarningScope.NODE: - # Node-based warnings may provide an override line. - if node is None: - raise InvalidMessageError( - "Message %s must provide Node, got None" % msgid - ) - - if line is None and node is not None: - line = node.fromlineno - if col_offset is None and hasattr(node, "col_offset"): - col_offset = ( - node.col_offset - ) # XXX measured in bytes for utf-8, divide by two for chars? - - # should this message be displayed - if not self.is_message_enabled(msgid, line, confidence): - self.file_state.handle_ignored_message( - self.get_message_state_scope(msgid, line, confidence), - msgid, - line, - node, - args, - confidence, - ) - return - # update stats - msg_cat = MSG_TYPES[msgid[0]] - self.msg_status |= MSG_TYPES_STATUS[msgid[0]] - self.stats[msg_cat] += 1 - self.stats["by_module"][self.current_name][msg_cat] += 1 - try: - self.stats["by_msg"][symbol] += 1 - except KeyError: - self.stats["by_msg"][symbol] = 1 - # expand message ? - msg = message_definition.msg - if args: - msg %= args - # get module and object - if node is None: - module, obj = self.current_name, "" - abspath = self.current_file - else: - module, obj = get_module_and_frameid(node) - abspath = node.root().file - path = abspath.replace(self.reporter.path_strip_prefix, "", 1) - # add the message - self.reporter.handle_message( - Message( - msgid, - symbol, - (abspath, path, module, obj, line or 1, col_offset or 0), - msg, - confidence, - ) - ) - - def print_full_documentation(self, stream=None): - """output a full documentation in ReST format""" - if not stream: - stream = sys.stdout - - print("Pylint global options and switches", file=stream) - print("----------------------------------", file=stream) - print("", file=stream) - print("Pylint provides global options and switches.", file=stream) - print("", file=stream) - - by_checker = {} - for checker in self.get_checkers(): - if checker.name == "master": - if checker.options: - for section, options in checker.options_by_section(): - if section is None: - title = "General options" - else: - title = "%s options" % section.capitalize() - print(title, file=stream) - print("~" * len(title), file=stream) - _rest_format_section(stream, None, options) - print("", file=stream) - else: - name = checker.name - try: - by_checker[name]["options"] += checker.options_and_values() - by_checker[name]["msgs"].update(checker.msgs) - by_checker[name]["reports"] += checker.reports - except KeyError: - by_checker[name] = { - "options": list(checker.options_and_values()), - "msgs": dict(checker.msgs), - "reports": list(checker.reports), - } - - print("Pylint checkers' options and switches", file=stream) - print("-------------------------------------", file=stream) - print("", file=stream) - print("Pylint checkers can provide three set of features:", file=stream) - print("", file=stream) - print("* options that control their execution,", file=stream) - print("* messages that they can raise,", file=stream) - print("* reports that they can generate.", file=stream) - print("", file=stream) - print("Below is a list of all checkers and their features.", file=stream) - print("", file=stream) - - for checker, info in sorted(by_checker.items()): - self._print_checker_doc(checker, info, stream=stream) - - @staticmethod - def _print_checker_doc(checker_name, info, stream=None): - """Helper method for print_full_documentation. - - Also used by doc/exts/pylint_extensions.py. - """ - if not stream: - stream = sys.stdout - - doc = info.get("doc") - module = info.get("module") - msgs = info.get("msgs") - options = info.get("options") - reports = info.get("reports") - - checker_title = "%s checker" % (checker_name.replace("_", " ").title()) - - if module: - # Provide anchor to link against - print(".. _%s:\n" % module, file=stream) - print(checker_title, file=stream) - print("~" * len(checker_title), file=stream) - print("", file=stream) - if module: - print("This checker is provided by ``%s``." % module, file=stream) - print("Verbatim name of the checker is ``%s``." % checker_name, file=stream) - print("", file=stream) - if doc: - # Provide anchor to link against - title = "{} Documentation".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) - print(cleandoc(doc), file=stream) - print("", file=stream) - if options: - title = "{} Options".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) - _rest_format_section(stream, None, options) - print("", file=stream) - if msgs: - title = "{} Messages".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) - for msgid, msg in sorted( - msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) - ): - msg = build_message_def(checker_name, msgid, msg) - print(msg.format_help(checkerref=False), file=stream) - print("", file=stream) - if reports: - title = "{} Reports".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) - for report in reports: - print(":%s: %s" % report[:2], file=stream) - print("", file=stream) - print("", file=stream) - - -class FileState: - """Hold internal state specific to the currently analyzed file""" - - def __init__(self, modname=None): - self.base_name = modname - self._module_msgs_state = {} - self._raw_module_msgs_state = {} - self._ignored_msgs = collections.defaultdict(set) - self._suppression_mapping = {} - self._effective_max_line_number = None - - def collect_block_lines(self, msgs_store, module_node): - """Walk the AST to collect block level options line numbers.""" - for msg, lines in self._module_msgs_state.items(): - self._raw_module_msgs_state[msg] = lines.copy() - orig_state = self._module_msgs_state.copy() - self._module_msgs_state = {} - self._suppression_mapping = {} - self._effective_max_line_number = module_node.tolineno - self._collect_block_lines(msgs_store, module_node, orig_state) - - def _collect_block_lines(self, msgs_store, node, msg_state): - """Recursively walk (depth first) AST to collect block level options - line numbers. - """ - for child in node.get_children(): - self._collect_block_lines(msgs_store, child, msg_state) - first = node.fromlineno - last = node.tolineno - # first child line number used to distinguish between disable - # which are the first child of scoped node with those defined later. - # For instance in the code below: - # - # 1. def meth8(self): - # 2. """test late disabling""" - # 3. # pylint: disable=E1102 - # 4. print self.blip - # 5. # pylint: disable=E1101 - # 6. print self.bla - # - # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6 - # - # this is necessary to disable locally messages applying to class / - # function using their fromlineno - if ( - isinstance(node, (nodes.Module, nodes.ClassDef, nodes.FunctionDef)) - and node.body - ): - firstchildlineno = node.body[0].fromlineno - else: - firstchildlineno = last - for msgid, lines in msg_state.items(): - for lineno, state in list(lines.items()): - original_lineno = lineno - if first > lineno or last < lineno: - continue - # Set state for all lines for this block, if the - # warning is applied to nodes. - message_definitions = msgs_store.get_message_definitions(msgid) - for message_definition in message_definitions: - if message_definition.scope == WarningScope.NODE: - if lineno > firstchildlineno: - state = True - first_, last_ = node.block_range(lineno) - else: - first_ = lineno - last_ = last - for line in range(first_, last_ + 1): - # do not override existing entries - if line in self._module_msgs_state.get(msgid, ()): - continue - if line in lines: # state change in the same block - state = lines[line] - original_lineno = line - if not state: - self._suppression_mapping[(msgid, line)] = original_lineno - try: - self._module_msgs_state[msgid][line] = state - except KeyError: - self._module_msgs_state[msgid] = {line: state} - del lines[lineno] - - def set_msg_status(self, msg, line, status): - """Set status (enabled/disable) for a given message at a given line""" - assert line > 0 - try: - self._module_msgs_state[msg.msgid][line] = status - except KeyError: - self._module_msgs_state[msg.msgid] = {line: status} - - def handle_ignored_message( - self, state_scope, msgid, line, node, args, confidence - ): # pylint: disable=unused-argument - """Report an ignored message. - - state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG, - depending on whether the message was disabled locally in the module, - or globally. The other arguments are the same as for add_message. - """ - if state_scope == MSG_STATE_SCOPE_MODULE: - try: - orig_line = self._suppression_mapping[(msgid, line)] - self._ignored_msgs[(msgid, orig_line)].add(line) - except KeyError: - pass - - def iter_spurious_suppression_messages(self, msgs_store): - for warning, lines in self._raw_module_msgs_state.items(): - for line, enable in lines.items(): - if not enable and (warning, line) not in self._ignored_msgs: - yield "useless-suppression", line, ( - msgs_store.get_msg_display_string(warning), - ) - # don't use iteritems here, _ignored_msgs may be modified by add_message - for (warning, from_), lines in list(self._ignored_msgs.items()): - for line in lines: - yield "suppressed-message", line, ( - msgs_store.get_msg_display_string(warning), - from_, - ) - - def get_effective_max_line_number(self): - return self._effective_max_line_number - - -class ReportsHandlerMixIn: - """a mix-in class containing all the reports and stats manipulation - related methods for the main lint class - """ - - def __init__(self): - self._reports = collections.defaultdict(list) - self._reports_state = {} - - def report_order(self): - """ Return a list of reports, sorted in the order - in which they must be called. - """ - return list(self._reports) - - def register_report(self, reportid, r_title, r_cb, checker): - """register a report - - reportid is the unique identifier for the report - r_title the report's title - r_cb the method to call to make the report - checker is the checker defining the report - """ - reportid = reportid.upper() - self._reports[checker].append((reportid, r_title, r_cb)) - - def enable_report(self, reportid): - """disable the report of the given id""" - reportid = reportid.upper() - self._reports_state[reportid] = True - - def disable_report(self, reportid): - """disable the report of the given id""" - reportid = reportid.upper() - self._reports_state[reportid] = False - - def report_is_enabled(self, reportid): - """return true if the report associated to the given identifier is - enabled - """ - return self._reports_state.get(reportid, True) - - def make_reports(self, stats, old_stats): - """render registered reports""" - sect = Section("Report", "%s statements analysed." % (self.stats["statement"])) - for checker in self.report_order(): - for reportid, r_title, r_cb in self._reports[checker]: - if not self.report_is_enabled(reportid): - continue - report_sect = Section(r_title) - try: - r_cb(report_sect, stats, old_stats) - except EmptyReportError: - continue - report_sect.report_id = reportid - sect.append(report_sect) - return sect - - def add_stats(self, **kwargs): - """add some stats entries to the statistic dictionary - raise an AssertionError if there is a key conflict - """ - for key, value in kwargs.items(): - if key[-1] == "_": - key = key[:-1] - assert key not in self.stats - self.stats[key] = value - return self.stats - - def _basename_in_blacklist_re(base_name, black_list_re): """Determines if the basename is matched in a regex blacklist @@ -947,81 +203,6 @@ def expand_modules(files_or_modules, black_list, black_list_re): return result, errors -class PyLintASTWalker: - def __init__(self, linter): - # callbacks per node types - self.nbstatements = 0 - self.visit_events = collections.defaultdict(list) - self.leave_events = collections.defaultdict(list) - self.linter = linter - - def _is_method_enabled(self, method): - if not hasattr(method, "checks_msgs"): - return True - for msg_desc in method.checks_msgs: - if self.linter.is_message_enabled(msg_desc): - return True - return False - - def add_checker(self, checker): - """walk to the checker's dir and collect visit and leave methods""" - # XXX : should be possible to merge needed_checkers and add_checker - vcids = set() - lcids = set() - visits = self.visit_events - leaves = self.leave_events - for member in dir(checker): - cid = member[6:] - if cid == "default": - continue - if member.startswith("visit_"): - v_meth = getattr(checker, member) - # don't use visit_methods with no activated message: - if self._is_method_enabled(v_meth): - visits[cid].append(v_meth) - vcids.add(cid) - elif member.startswith("leave_"): - l_meth = getattr(checker, member) - # don't use leave_methods with no activated message: - if self._is_method_enabled(l_meth): - leaves[cid].append(l_meth) - lcids.add(cid) - visit_default = getattr(checker, "visit_default", None) - if visit_default: - for cls in nodes.ALL_NODE_CLASSES: - cid = cls.__name__.lower() - if cid not in vcids: - visits[cid].append(visit_default) - # for now we have no "leave_default" method in Pylint - - def walk(self, astroid): - """call visit events of astroid checkers for the given node, recurse on - its children, then leave events. - """ - cid = astroid.__class__.__name__.lower() - - # Detect if the node is a new name for a deprecated alias. - # In this case, favour the methods for the deprecated - # alias if any, in order to maintain backwards - # compatibility. - visit_events = self.visit_events.get(cid, ()) - leave_events = self.leave_events.get(cid, ()) - - if astroid.is_statement: - self.nbstatements += 1 - # generate events for this node on each checker - for cb in visit_events or (): - cb(astroid) - # recurse on children - for child in astroid.get_children(): - self.walk(child) - for cb in leave_events or (): - cb(astroid) - - -PY_EXTS = (".py", ".pyc", ".pyo", ".pyw", ".so", ".dll") - - def register_plugins(linter, directory): """load all module and package in the given directory, looking for a 'register' function in each one, used to register pylint checkers @@ -1134,15 +315,6 @@ def _unquote(string): return string -def _normalize_text(text, line_len=80, indent=""): - """Wrap the text on the given line length.""" - return "\n".join( - textwrap.wrap( - text, width=line_len, initial_indent=indent, subsequent_indent=indent - ) - ) - - def _check_csv(value): if isinstance(value, (list, tuple)): return value @@ -1185,7 +357,7 @@ def _ini_format(stream, options): value = _format_option_value(optdict, value) help_opt = optdict.get("help") if help_opt: - help_opt = _normalize_text(help_opt, line_len=79, indent="# ") + help_opt = normalize_text(help_opt, line_len=79, indent="# ") print(file=stream) print(help_opt, file=stream) else: @@ -1210,13 +382,13 @@ def _rest_format_section(stream, section, options, doc=None): if section: print("%s\n%s" % (section, "'" * len(section)), file=stream) if doc: - print(_normalize_text(doc, line_len=79, indent=""), file=stream) + print(normalize_text(doc, line_len=79, indent=""), file=stream) print(file=stream) for optname, optdict, value in options: help_opt = optdict.get("help") print(":%s:" % optname, file=stream) if help_opt: - help_opt = _normalize_text(help_opt, line_len=79, indent=" ") + help_opt = normalize_text(help_opt, line_len=79, indent=" ") print(help_opt, file=stream) if value: value = str(_format_option_value(optdict, value)) diff --git a/pylint/utils/warning_scope.py b/pylint/utils/warning_scope.py new file mode 100644 index 0000000000..bdad94d3fb --- /dev/null +++ b/pylint/utils/warning_scope.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +# 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 + + +class WarningScope: + LINE = "line-based-msg" + NODE = "node-based-msg" From e9a12d27cffbb16546d6b479d2d548070e8c3579 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 18 Dec 2018 23:43:03 +0100 Subject: [PATCH 0031/5147] Fix - C0412: Imports from package os are not grouped --- pylint/utils/utils.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index 6c6cd269bb..4ccb1159f2 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -6,12 +6,11 @@ from __future__ import print_function import codecs -import collections -import os import re import sys import tokenize import warnings +from os import linesep, listdir from os.path import basename, dirname, exists, isdir, join, normpath, splitext from astroid import Module, modutils @@ -124,9 +123,9 @@ def expand_modules(files_or_modules, black_list, black_list_re): result = [] errors = [] for something in files_or_modules: - if os.path.basename(something) in black_list: + if basename(something) in black_list: continue - if _basename_in_blacklist_re(os.path.basename(something), black_list_re): + if _basename_in_blacklist_re(basename(something), black_list_re): continue if exists(something): # this is a file or a directory @@ -208,7 +207,7 @@ def register_plugins(linter, directory): 'register' function in each one, used to register pylint checkers """ imported = {} - for filename in os.listdir(directory): + for filename in listdir(directory): base, extension = splitext(filename) if base in imported or base == "__pycache__": continue @@ -324,7 +323,7 @@ def _check_csv(value): def _comment(string): """return string as a comment""" lines = [line.strip() for line in string.splitlines()] - return "# " + ("%s# " % os.linesep).join(lines) + return "# " + ("%s# " % linesep).join(lines) def _format_option_value(optdict, value): From b1780d1a4f88d4a94c162780ef346ad66b44b172 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Fri, 8 Mar 2019 07:56:09 +0100 Subject: [PATCH 0032/5147] Refactor - Move _rest_format_section in message_handler_mix_in.py Following review see : https://github.com/PyCQA/pylint/pull/2654#issuecomment-470748956 --- pylint/utils/message_handler_mix_in.py | 22 +++++++++++++++++++++- pylint/utils/utils.py | 19 ------------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/pylint/utils/message_handler_mix_in.py b/pylint/utils/message_handler_mix_in.py index 4b817db81a..408bacabfe 100644 --- a/pylint/utils/message_handler_mix_in.py +++ b/pylint/utils/message_handler_mix_in.py @@ -22,13 +22,33 @@ from pylint.utils.message import Message from pylint.utils.utils import ( WarningScope, - _rest_format_section, + _format_option_value, build_message_def, category_id, get_module_and_frameid, + normalize_text, ) +def _rest_format_section(stream, section, options, doc=None): + """format an options section using as ReST formatted output""" + if section: + print("%s\n%s" % (section, "'" * len(section)), file=stream) + if doc: + print(normalize_text(doc, line_len=79, indent=""), file=stream) + print(file=stream) + for optname, optdict, value in options: + help_opt = optdict.get("help") + print(":%s:" % optname, file=stream) + if help_opt: + help_opt = normalize_text(help_opt, line_len=79, indent=" ") + print(help_opt, file=stream) + if value: + value = str(_format_option_value(optdict, value)) + print(file=stream) + print(" Default: ``%s``" % value.replace("`` ", "```` ``"), file=stream) + + class MessagesHandlerMixIn: """a mix-in class containing all the messages related methods for the main lint class diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index 4ccb1159f2..165a35c84b 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -374,22 +374,3 @@ def _ini_format(stream, options): format_section = _ini_format_section - - -def _rest_format_section(stream, section, options, doc=None): - """format an options section using as ReST formatted output""" - if section: - print("%s\n%s" % (section, "'" * len(section)), file=stream) - if doc: - print(normalize_text(doc, line_len=79, indent=""), file=stream) - print(file=stream) - for optname, optdict, value in options: - help_opt = optdict.get("help") - print(":%s:" % optname, file=stream) - if help_opt: - help_opt = normalize_text(help_opt, line_len=79, indent=" ") - print(help_opt, file=stream) - if value: - value = str(_format_option_value(optdict, value)) - print(file=stream) - print(" Default: ``%s``" % value.replace("`` ", "```` ``"), file=stream) From 2b5074986763d9a508a2dc38c99ba839612dc672 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Fri, 8 Mar 2019 19:49:30 +0100 Subject: [PATCH 0033/5147] Refactor - Create a pylint.message package There is a lot of Message related class in Utils this warrant the creation of a new package. See also review for burst utils.py into a package here: https://github.com/PyCQA/pylint/pull/2654#issuecomment-470748956 --- pylint/checkers/misc.py | 3 +- pylint/lint.py | 35 +++++++----- pylint/message/__init__.py | 55 +++++++++++++++++++ pylint/message/build_message_definition.py | 34 ++++++++++++ pylint/message/constants.py | 24 ++++++++ pylint/{utils => message}/message.py | 2 +- .../{utils => message}/message_definition.py | 2 +- .../message_handler_mix_in.py | 8 +-- pylint/{utils => message}/message_store.py | 2 +- pylint/test/unittest_lint.py | 5 +- pylint/test/utils/unittest_utils.py | 11 ++-- pylint/utils/__init__.py | 16 +----- pylint/utils/constants.py | 20 ------- pylint/utils/file_state.py | 2 +- pylint/utils/utils.py | 31 +---------- 15 files changed, 155 insertions(+), 95 deletions(-) create mode 100644 pylint/message/__init__.py create mode 100644 pylint/message/build_message_definition.py create mode 100644 pylint/message/constants.py rename pylint/{utils => message}/message.py (96%) rename pylint/{utils => message}/message_definition.py (98%) rename pylint/{utils => message}/message_handler_mix_in.py (98%) rename pylint/{utils => message}/message_store.py (99%) diff --git a/pylint/checkers/misc.py b/pylint/checkers/misc.py index a06b9a1812..26dc0a3df0 100644 --- a/pylint/checkers/misc.py +++ b/pylint/checkers/misc.py @@ -26,7 +26,8 @@ from pylint.interfaces import IRawChecker, ITokenChecker from pylint.checkers import BaseChecker -from pylint.utils import OPTION_RGX, MessagesHandlerMixIn +from pylint.utils import OPTION_RGX +from pylint.message import MessagesHandlerMixIn class ByIdManagedMessagesChecker(BaseChecker): diff --git a/pylint/lint.py b/pylint/lint.py index 0b273ea71c..5aa3bd0c78 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -81,8 +81,15 @@ from pylint import checkers from pylint import interfaces from pylint import reporters +from pylint.message import MessagesStore, Message, MSG_TYPES, MessagesHandlerMixIn +from pylint.utils import ( + FileState, + PyLintASTWalker, + ReportsHandlerMixIn, + OPTION_RGX, + utils, +) from pylint import exceptions -from pylint import utils from pylint import config from pylint.__pkginfo__ import version from pylint.reporters.ureports import nodes as report_nodes @@ -308,8 +315,8 @@ def _run_linter(self, file_or_module): # pylint: disable=too-many-instance-attributes class PyLinter( config.OptionsManagerMixIn, - utils.MessagesHandlerMixIn, - utils.ReportsHandlerMixIn, + MessagesHandlerMixIn, + ReportsHandlerMixIn, checkers.BaseTokenChecker, ): """lint Python modules using external checkers. @@ -594,7 +601,7 @@ def __init__(self, options=(), reporter=None, option_groups=(), pylintrc=None): # some stuff has to be done before ancestors initialization... # # messages store / checkers / reporter / astroid manager - self.msgs_store = utils.MessagesStore() + self.msgs_store = MessagesStore() self.reporter = None self._reporter_name = None self._reporters = {} @@ -602,7 +609,7 @@ def __init__(self, options=(), reporter=None, option_groups=(), pylintrc=None): self._pragma_lineno = {} self._ignore_file = False # visit variables - self.file_state = utils.FileState() + self.file_state = FileState() self.current_name = None self.current_file = None self.stats = None @@ -620,8 +627,8 @@ def __init__(self, options=(), reporter=None, option_groups=(), pylintrc=None): astroid_version, sys.version, ) - utils.MessagesHandlerMixIn.__init__(self) - utils.ReportsHandlerMixIn.__init__(self) + MessagesHandlerMixIn.__init__(self) + ReportsHandlerMixIn.__init__(self) super(PyLinter, self).__init__( usage=__doc__, version=full_version, config_file=pylintrc or config.PYLINTRC ) @@ -836,7 +843,7 @@ def process_tokens(self, tokens): for (tok_type, content, start, _, _) in tokens: if tok_type != tokenize.COMMENT: continue - match = utils.OPTION_RGX.search(content) + match = OPTION_RGX.search(content) if match is None: continue @@ -1049,7 +1056,7 @@ def _parallel_check(self, files_or_modules): (_, self.file_state.base_name, module, messages, stats, msg_status) = result for msg in messages: - msg = utils.Message(*msg) + msg = Message(*msg) self.set_current_module(module) self.reporter.handle_message(msg) @@ -1065,7 +1072,7 @@ def _parallel_check(self, files_or_modules): checker.stats = self.stats def _do_check(self, files_or_modules): - walker = utils.PyLintASTWalker(self) + walker = PyLintASTWalker(self) _checkers = self.prepare_checkers() tokencheckers = [ c @@ -1102,7 +1109,7 @@ def _do_check(self, files_or_modules): ast_node = _ast_from_string(_read_stdin(), filepath, modname) if ast_node is not None: - self.file_state = utils.FileState(filepath) + self.file_state = FileState(filepath) self.check_astroid_module(ast_node, walker, rawcheckers, tokencheckers) # warn about spurious inline messages handling spurious_messages = self.file_state.iter_spurious_suppression_messages( @@ -1124,7 +1131,7 @@ def _do_check(self, files_or_modules): # XXX to be correct we need to keep module_msgs_state for every # analyzed module (the problem stands with localized messages which # are only detected in the .close step) - self.file_state = utils.FileState(descr["basename"]) + self.file_state = FileState(descr["basename"]) self._ignore_file = False # fix the current file (if the source file was not available or # if it's actually a c extension) @@ -1167,7 +1174,7 @@ def set_current_module(self, modname, filepath=None): self.current_file = filepath or modname self.stats["by_module"][modname] = {} self.stats["by_module"][modname]["statement"] = 0 - for msg_cat in utils.MSG_TYPES.values(): + for msg_cat in MSG_TYPES.values(): self.stats["by_module"][modname][msg_cat] = 0 def get_ast(self, filepath, modname): @@ -1223,7 +1230,7 @@ def open(self): MANAGER.always_load_extensions = self.config.unsafe_load_any_extension MANAGER.max_inferable_values = self.config.limit_inference_results MANAGER.extension_package_whitelist.update(self.config.extension_pkg_whitelist) - for msg_cat in utils.MSG_TYPES.values(): + for msg_cat in MSG_TYPES.values(): self.stats[msg_cat] = 0 def generate_reports(self): diff --git a/pylint/message/__init__.py b/pylint/message/__init__.py new file mode 100644 index 0000000000..694e2eb136 --- /dev/null +++ b/pylint/message/__init__.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2009 Vincent +# Copyright (c) 2009 Mads Kiilerich +# Copyright (c) 2012-2014 Google, Inc. +# Copyright (c) 2014-2018 Claudiu Popa +# Copyright (c) 2014-2015 Michal Nowikowski +# Copyright (c) 2014 LCD 47 +# Copyright (c) 2014 Brett Cannon +# Copyright (c) 2014 Arun Persaud +# Copyright (c) 2014 Damien Nozay +# Copyright (c) 2015 Aru Sahni +# Copyright (c) 2015 Florian Bruhin +# Copyright (c) 2015 Simu Toni +# Copyright (c) 2015 Ionel Cristian Maries +# Copyright (c) 2016 Łukasz Rogalski +# Copyright (c) 2016 Moises Lopez +# Copyright (c) 2016 Glenn Matthews +# Copyright (c) 2016 Glenn Matthews +# Copyright (c) 2016 Ashley Whetter +# Copyright (c) 2016 xmo-odoo +# Copyright (c) 2017-2018 hippo91 +# Copyright (c) 2017 Pierre Sassoulas +# Copyright (c) 2017 Bryce Guinta +# Copyright (c) 2017 Chris Lamb +# Copyright (c) 2017 Anthony Sottile +# Copyright (c) 2017 Thomas Hisch +# Copyright (c) 2017 Mikhail Fesenko +# Copyright (c) 2017 Craig Citro +# Copyright (c) 2017 Ville Skyttä +# Copyright (c) 2018 ssolanki +# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com> +# Copyright (c) 2018 Pierre Sassoulas +# Copyright (c) 2018 Reverb C +# Copyright (c) 2018 Nick Drozd + +# 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 + +"""All the classes related to Message handling.""" + +from pylint.message.constants import ( + MSG_STATE_CONFIDENCE, + _SCOPE_EXEMPT, + MSG_STATE_SCOPE_CONFIG, + MSG_STATE_SCOPE_MODULE, + MSG_TYPES, + MSG_TYPES_LONG, + MSG_TYPES_STATUS, +) +from pylint.message.build_message_definition import build_message_def +from pylint.message.message import Message +from pylint.message.message_definition import MessageDefinition +from pylint.message.message_handler_mix_in import MessagesHandlerMixIn +from pylint.message.message_store import MessagesStore diff --git a/pylint/message/build_message_definition.py b/pylint/message/build_message_definition.py new file mode 100644 index 0000000000..50c1152c3c --- /dev/null +++ b/pylint/message/build_message_definition.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +# 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 + +import warnings + +from pylint.interfaces import IRawChecker, ITokenChecker, implements +from pylint.message.message_definition import MessageDefinition +from pylint.utils.warning_scope import WarningScope + + +def build_message_def(checker, msgid, msg_tuple): + if implements(checker, (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: + # messages should have a symbol, but for backward compatibility + # they may not. + (msg, descr) = msg_tuple + warnings.warn( + "[pylint 0.26] description of message %s doesn't include " + "a symbolic name" % msgid, + DeprecationWarning, + ) + symbol = None + options.setdefault("scope", default_scope) + return MessageDefinition(checker, msgid, msg, descr, symbol, **options) diff --git a/pylint/message/constants.py b/pylint/message/constants.py new file mode 100644 index 0000000000..37fd973386 --- /dev/null +++ b/pylint/message/constants.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +# 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 + +MSG_STATE_CONFIDENCE = 2 +_MSG_ORDER = "EWRCIF" +MSG_STATE_SCOPE_CONFIG = 0 +MSG_STATE_SCOPE_MODULE = 1 + +# The line/node distinction does not apply to fatal errors and reports. +_SCOPE_EXEMPT = "FR" + +MSG_TYPES = { + "I": "info", + "C": "convention", + "R": "refactor", + "W": "warning", + "E": "error", + "F": "fatal", +} +MSG_TYPES_LONG = {v: k for k, v in MSG_TYPES.items()} + +MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1} diff --git a/pylint/utils/message.py b/pylint/message/message.py similarity index 96% rename from pylint/utils/message.py rename to pylint/message/message.py index ea1b9ae7ad..f4a5376007 100644 --- a/pylint/utils/message.py +++ b/pylint/message/message.py @@ -6,7 +6,7 @@ import collections -from pylint.utils.constants import MSG_TYPES +from pylint.message.constants import MSG_TYPES _MsgBase = collections.namedtuple( "_MsgBase", diff --git a/pylint/utils/message_definition.py b/pylint/message/message_definition.py similarity index 98% rename from pylint/utils/message_definition.py rename to pylint/message/message_definition.py index 6f8c32fc38..257f7ce3a0 100644 --- a/pylint/utils/message_definition.py +++ b/pylint/message/message_definition.py @@ -6,7 +6,7 @@ import sys from pylint.exceptions import InvalidMessageError -from pylint.utils.constants import MSG_TYPES +from pylint.message.constants import MSG_TYPES from pylint.utils.normalize_text import normalize_text diff --git a/pylint/utils/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py similarity index 98% rename from pylint/utils/message_handler_mix_in.py rename to pylint/message/message_handler_mix_in.py index 408bacabfe..d01be8ff3a 100644 --- a/pylint/utils/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -10,7 +10,7 @@ from pylint.exceptions import InvalidMessageError, UnknownMessageError from pylint.interfaces import UNDEFINED -from pylint.utils.constants import ( +from pylint.message.constants import ( _MSG_ORDER, _SCOPE_EXEMPT, MSG_STATE_CONFIDENCE, @@ -19,11 +19,11 @@ MSG_TYPES, MSG_TYPES_STATUS, ) -from pylint.utils.message import Message +from pylint.message.message import Message +from pylint.utils.warning_scope import WarningScope +from pylint.message.build_message_definition import build_message_def from pylint.utils.utils import ( - WarningScope, _format_option_value, - build_message_def, category_id, get_module_and_frameid, normalize_text, diff --git a/pylint/utils/message_store.py b/pylint/message/message_store.py similarity index 99% rename from pylint/utils/message_store.py rename to pylint/message/message_store.py index 80c66e483a..3c34b6f246 100644 --- a/pylint/utils/message_store.py +++ b/pylint/message/message_store.py @@ -8,7 +8,7 @@ import collections from pylint.exceptions import InvalidMessageError, UnknownMessageError -from pylint.utils.utils import build_message_def +from pylint.message.build_message_definition import build_message_def class MessagesStore: diff --git a/pylint/test/unittest_lint.py b/pylint/test/unittest_lint.py index 4d985192e7..efd9d07a97 100644 --- a/pylint/test/unittest_lint.py +++ b/pylint/test/unittest_lint.py @@ -39,15 +39,14 @@ from pylint import config, lint from pylint.lint import PyLinter, Run, preprocess_options, ArgumentPreprocessingError -from pylint.utils import ( +from pylint.message import ( MSG_STATE_SCOPE_CONFIG, MSG_STATE_SCOPE_MODULE, MSG_STATE_CONFIDENCE, MessagesStore, MessageDefinition, - FileState, - tokenize_module, ) +from pylint.utils import FileState, tokenize_module from pylint.exceptions import InvalidMessageError, UnknownMessageError import pylint.testutils as testutils from pylint.reporters import text diff --git a/pylint/test/utils/unittest_utils.py b/pylint/test/utils/unittest_utils.py index f295d9354d..740296dda9 100644 --- a/pylint/test/utils/unittest_utils.py +++ b/pylint/test/utils/unittest_utils.py @@ -22,7 +22,8 @@ import astroid -from pylint import utils +from pylint.utils import utils, PyLintASTWalker +from pylint.message import MessagesStore, MessageDefinition from pylint.checkers.utils import check_messages, get_node_last_lineno from pylint.exceptions import InvalidMessageError import pytest @@ -60,7 +61,7 @@ def test_check_messages(self): linter = self.MockLinter( {"first-message": True, "second-message": False, "third-message": True} ) - walker = utils.PyLintASTWalker(linter) + walker = PyLintASTWalker(linter) checker = self.Checker() walker.add_checker(checker) walker.walk(astroid.parse("x = func()")) @@ -76,7 +77,7 @@ def visit_assname(self, node): self.called = True linter = self.MockLinter({"first-message": True}) - walker = utils.PyLintASTWalker(linter) + walker = PyLintASTWalker(linter) checker = Checker() walker.add_checker(checker) with warnings.catch_warnings(record=True): @@ -100,7 +101,7 @@ def test__basename_in_blacklist_re_nomatch(): @pytest.fixture def store(): - return utils.MessagesStore() + return MessagesStore() @pytest.mark.parametrize( @@ -240,7 +241,7 @@ class CheckerTwo(object): ) def test_create_invalid_message_type(msgid, expected): with pytest.raises(InvalidMessageError) as cm: - utils.MessageDefinition("checker", msgid, "msg", "descr", "symbol", "scope") + MessageDefinition("checker", msgid, "msg", "descr", "symbol", "scope") assert str(cm.value) == expected diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index d8cfa9d130..0a4f6fd3c2 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -41,21 +41,8 @@ main pylint class """ -from pylint.utils.constants import ( - MSG_STATE_CONFIDENCE, - MSG_STATE_SCOPE_CONFIG, - MSG_STATE_SCOPE_MODULE, - MSG_TYPES, - MSG_TYPES_LONG, - MSG_TYPES_STATUS, - OPTION_RGX, - PY_EXTS, -) +from pylint.utils.constants import OPTION_RGX, PY_EXTS from pylint.utils.file_state import FileState -from pylint.utils.message import Message -from pylint.utils.message_definition import MessageDefinition -from pylint.utils.message_handler_mix_in import MessagesHandlerMixIn -from pylint.utils.message_store import MessagesStore from pylint.utils.normalize_text import normalize_text from pylint.utils.pylint_ast_walker import PyLintASTWalker from pylint.utils.reports_handler_mix_in import ReportsHandlerMixIn @@ -65,7 +52,6 @@ _format_option_value, _splitstrip, _unquote, - build_message_def, category_id, decoding_stream, deprecated_option, diff --git a/pylint/utils/constants.py b/pylint/utils/constants.py index 4c1a192f7c..46a7e1be4f 100644 --- a/pylint/utils/constants.py +++ b/pylint/utils/constants.py @@ -5,29 +5,9 @@ import re -MSG_STATE_CONFIDENCE = 2 -_MSG_ORDER = "EWRCIF" -MSG_STATE_SCOPE_CONFIG = 0 -MSG_STATE_SCOPE_MODULE = 1 - -# The line/node distinction does not apply to fatal errors and reports. -_SCOPE_EXEMPT = "FR" - # Allow stopping after the first semicolon/hash encountered, # so that an option can be continued with the reasons # why it is active or disabled. OPTION_RGX = re.compile(r"\s*#.*\bpylint:\s*([^;#]+)[;#]{0,1}") PY_EXTS = (".py", ".pyc", ".pyo", ".pyw", ".so", ".dll") - -MSG_TYPES = { - "I": "info", - "C": "convention", - "R": "refactor", - "W": "warning", - "E": "error", - "F": "fatal", -} -MSG_TYPES_LONG = {v: k for k, v in MSG_TYPES.items()} - -MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1} diff --git a/pylint/utils/file_state.py b/pylint/utils/file_state.py index 6eefff6ec0..46180833a9 100644 --- a/pylint/utils/file_state.py +++ b/pylint/utils/file_state.py @@ -6,7 +6,7 @@ import collections from astroid import nodes -from pylint.utils.constants import MSG_STATE_SCOPE_MODULE +from pylint.message.constants import MSG_STATE_SCOPE_MODULE from pylint.utils.warning_scope import WarningScope diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index 165a35c84b..1eb279346d 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -9,16 +9,13 @@ import re import sys import tokenize -import warnings from os import linesep, listdir from os.path import basename, dirname, exists, isdir, join, normpath, splitext from astroid import Module, modutils -from pylint.interfaces import IRawChecker, ITokenChecker, implements -from pylint.utils.constants import MSG_TYPES, MSG_TYPES_LONG, PY_EXTS -from pylint.utils.message_definition import MessageDefinition +from pylint.utils.constants import PY_EXTS +from pylint.message.constants import MSG_TYPES, MSG_TYPES_LONG from pylint.utils.normalize_text import normalize_text -from pylint.utils.warning_scope import WarningScope def get_module_and_frameid(node): @@ -67,30 +64,6 @@ def tokenize_module(module): return list(tokenize.tokenize(readline)) -def build_message_def(checker, msgid, msg_tuple): - if implements(checker, (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: - # messages should have a symbol, but for backward compatibility - # they may not. - (msg, descr) = msg_tuple - warnings.warn( - "[pylint 0.26] description of message %s doesn't include " - "a symbolic name" % msgid, - DeprecationWarning, - ) - symbol = None - options.setdefault("scope", default_scope) - return MessageDefinition(checker, msgid, msg, descr, symbol, **options) - - def _basename_in_blacklist_re(base_name, black_list_re): """Determines if the basename is matched in a regex blacklist From bbfd38f52b6b62c4eb2a4dfda0363c85908e6104 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Fri, 8 Mar 2019 20:03:54 +0100 Subject: [PATCH 0034/5147] Refactor - Create a unittest file for the pylint.message package --- pylint/test/message/unittest_message.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 pylint/test/message/unittest_message.py diff --git a/pylint/test/message/unittest_message.py b/pylint/test/message/unittest_message.py new file mode 100644 index 0000000000..2e1bb6c980 --- /dev/null +++ b/pylint/test/message/unittest_message.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +# 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 + +import io + +import astroid + +from pylint.message import MessagesStore, MessageDefinition +from pylint.checkers.utils import check_messages, get_node_last_lineno +from pylint.exceptions import InvalidMessageError +import pytest From 21954deb237d6eac162a1360f969f1b58ea354a7 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Fri, 8 Mar 2019 20:05:32 +0100 Subject: [PATCH 0035/5147] Refactor - Move test_create_invalid_message_type in pylint.message test --- pylint/test/message/unittest_message.py | 13 +++++++++++++ pylint/test/utils/unittest_utils.py | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pylint/test/message/unittest_message.py b/pylint/test/message/unittest_message.py index 2e1bb6c980..42bec155a4 100644 --- a/pylint/test/message/unittest_message.py +++ b/pylint/test/message/unittest_message.py @@ -11,3 +11,16 @@ from pylint.checkers.utils import check_messages, get_node_last_lineno from pylint.exceptions import InvalidMessageError import pytest + + +@pytest.mark.parametrize( + "msgid,expected", + [ + ("Q1234", "Bad message type Q in 'Q1234'"), + ("W12345", "Invalid message id 'W12345'"), + ], +) +def test_create_invalid_message_type(msgid, expected): + with pytest.raises(InvalidMessageError) as cm: + MessageDefinition("checker", msgid, "msg", "descr", "symbol", "scope") + assert str(cm.value) == expected diff --git a/pylint/test/utils/unittest_utils.py b/pylint/test/utils/unittest_utils.py index 740296dda9..d3e21d1cd3 100644 --- a/pylint/test/utils/unittest_utils.py +++ b/pylint/test/utils/unittest_utils.py @@ -232,19 +232,6 @@ class CheckerTwo(object): ) -@pytest.mark.parametrize( - "msgid,expected", - [ - ("Q1234", "Bad message type Q in 'Q1234'"), - ("W12345", "Invalid message id 'W12345'"), - ], -) -def test_create_invalid_message_type(msgid, expected): - with pytest.raises(InvalidMessageError) as cm: - MessageDefinition("checker", msgid, "msg", "descr", "symbol", "scope") - assert str(cm.value) == expected - - def test_decoding_stream_unknown_encoding(): """decoding_stream should fall back to *some* decoding when given an unknown encoding. From 02e0e37a19c0999a2159b090daa0545a97dee2f6 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Fri, 8 Mar 2019 20:17:11 +0100 Subject: [PATCH 0036/5147] Refactor - Move test_register_error in pylint.message test --- pylint/test/message/unittest_message.py | 141 +++++++++++++++++++++++- pylint/test/utils/unittest_utils.py | 136 ----------------------- 2 files changed, 135 insertions(+), 142 deletions(-) diff --git a/pylint/test/message/unittest_message.py b/pylint/test/message/unittest_message.py index 42bec155a4..311e4af192 100644 --- a/pylint/test/message/unittest_message.py +++ b/pylint/test/message/unittest_message.py @@ -3,14 +3,143 @@ # 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 -import io - -import astroid +import pytest -from pylint.message import MessagesStore, MessageDefinition -from pylint.checkers.utils import check_messages, get_node_last_lineno from pylint.exceptions import InvalidMessageError -import pytest +from pylint.message import MessageDefinition, MessagesStore + + +@pytest.fixture +def store(): + return MessagesStore() + + +@pytest.mark.parametrize( + "messages,expected", + [ + ( + { + "W1234": ("message one", "msg-symbol-one", "msg description"), + "W4321": ("message two", "msg-symbol-two", "msg description"), + }, + r"Inconsistent checker part in message id 'W4321' (expected 'x12xx' because we already had ['W1234']).", + ), + ( + { + "W1233": ( + "message two", + "msg-symbol-two", + "msg description", + {"old_names": [("W1234", "old-symbol")]}, + ), + "W1234": ("message one", "msg-symbol-one", "msg description"), + }, + "Message id 'W1234' cannot have both 'msg-symbol-one' and 'old-symbol' as symbolic name.", + ), + ( + { + "W1234": ("message one", "msg-symbol-one", "msg description"), + "W1235": ( + "message two", + "msg-symbol-two", + "msg description", + {"old_names": [("W1234", "old-symbol")]}, + ), + }, + "Message id 'W1234' cannot have both 'msg-symbol-one' and 'old-symbol' as symbolic name.", + ), + ( + { + "W1234": ( + "message one", + "msg-symbol-one", + "msg description", + {"old_names": [("W1201", "old-symbol-one")]}, + ), + "W1235": ( + "message two", + "msg-symbol-two", + "msg description", + {"old_names": [("W1201", "old-symbol-two")]}, + ), + }, + "Message id 'W1201' cannot have both 'old-symbol-one' and 'old-symbol-two' as symbolic name.", + ), + ( + { + "W1234": ("message one", "msg-symbol", "msg description"), + "W1235": ("message two", "msg-symbol", "msg description"), + }, + "Message symbol 'msg-symbol' cannot be used for 'W1234' and 'W1235' at the same time.", + ), + ( + { + "W1233": ( + "message two", + "msg-symbol-two", + "msg description", + {"old_names": [("W1230", "msg-symbol-one")]}, + ), + "W1234": ("message one", "msg-symbol-one", "msg description"), + }, + "Message symbol 'msg-symbol-one' cannot be used for 'W1230' and 'W1234' at the same time.", + ), + ( + { + "W1234": ("message one", "msg-symbol-one", "msg description"), + "W1235": ( + "message two", + "msg-symbol-two", + "msg description", + {"old_names": [("W1230", "msg-symbol-one")]}, + ), + }, + "Message symbol 'msg-symbol-one' cannot be used for 'W1234' and 'W1235' at the same time.", + ), + ( + { + "W1234": ( + "message one", + "msg-symbol-one", + "msg description", + {"old_names": [("W1230", "old-symbol-one")]}, + ), + "W1235": ( + "message two", + "msg-symbol-two", + "msg description", + {"old_names": [("W1231", "old-symbol-one")]}, + ), + }, + "Message symbol 'old-symbol-one' cannot be used for 'W1230' and 'W1235' at the same time.", + ), + ], +) +def test_register_error(store, messages, expected): + class Checker(object): + name = "checker" + msgs = messages + + with pytest.raises(InvalidMessageError) as cm: + store.register_messages_from_checker(Checker()) + assert str(cm.value) == expected + + +def test_register_error_new_id_duplicate_of_new(store): + class CheckerOne(object): + name = "checker_one" + msgs = {"W1234": ("message one", "msg-symbol-one", "msg description.")} + + class CheckerTwo(object): + name = "checker_two" + msgs = {"W1234": ("message two", "msg-symbol-two", "another msg description.")} + + store.register_messages_from_checker(CheckerOne()) + test_register_error( + store, + {"W1234": ("message two", "msg-symbol-two", "another msg description.")}, + "Message id 'W1234' cannot have both 'msg-symbol-one' and 'msg-symbol-two' as symbolic name.", + ) @pytest.mark.parametrize( diff --git a/pylint/test/utils/unittest_utils.py b/pylint/test/utils/unittest_utils.py index d3e21d1cd3..e593c3aec1 100644 --- a/pylint/test/utils/unittest_utils.py +++ b/pylint/test/utils/unittest_utils.py @@ -23,10 +23,7 @@ import astroid from pylint.utils import utils, PyLintASTWalker -from pylint.message import MessagesStore, MessageDefinition from pylint.checkers.utils import check_messages, get_node_last_lineno -from pylint.exceptions import InvalidMessageError -import pytest class TestPyLintASTWalker(object): @@ -99,139 +96,6 @@ def test__basename_in_blacklist_re_nomatch(): assert not utils._basename_in_blacklist_re("enchilad.py", patterns) -@pytest.fixture -def store(): - return MessagesStore() - - -@pytest.mark.parametrize( - "messages,expected", - [ - ( - { - "W1234": ("message one", "msg-symbol-one", "msg description"), - "W4321": ("message two", "msg-symbol-two", "msg description"), - }, - r"Inconsistent checker part in message id 'W4321' (expected 'x12xx' because we already had ['W1234']).", - ), - ( - { - "W1233": ( - "message two", - "msg-symbol-two", - "msg description", - {"old_names": [("W1234", "old-symbol")]}, - ), - "W1234": ("message one", "msg-symbol-one", "msg description"), - }, - "Message id 'W1234' cannot have both 'msg-symbol-one' and 'old-symbol' as symbolic name.", - ), - ( - { - "W1234": ("message one", "msg-symbol-one", "msg description"), - "W1235": ( - "message two", - "msg-symbol-two", - "msg description", - {"old_names": [("W1234", "old-symbol")]}, - ), - }, - "Message id 'W1234' cannot have both 'msg-symbol-one' and 'old-symbol' as symbolic name.", - ), - ( - { - "W1234": ( - "message one", - "msg-symbol-one", - "msg description", - {"old_names": [("W1201", "old-symbol-one")]}, - ), - "W1235": ( - "message two", - "msg-symbol-two", - "msg description", - {"old_names": [("W1201", "old-symbol-two")]}, - ), - }, - "Message id 'W1201' cannot have both 'old-symbol-one' and 'old-symbol-two' as symbolic name.", - ), - ( - { - "W1234": ("message one", "msg-symbol", "msg description"), - "W1235": ("message two", "msg-symbol", "msg description"), - }, - "Message symbol 'msg-symbol' cannot be used for 'W1234' and 'W1235' at the same time.", - ), - ( - { - "W1233": ( - "message two", - "msg-symbol-two", - "msg description", - {"old_names": [("W1230", "msg-symbol-one")]}, - ), - "W1234": ("message one", "msg-symbol-one", "msg description"), - }, - "Message symbol 'msg-symbol-one' cannot be used for 'W1230' and 'W1234' at the same time.", - ), - ( - { - "W1234": ("message one", "msg-symbol-one", "msg description"), - "W1235": ( - "message two", - "msg-symbol-two", - "msg description", - {"old_names": [("W1230", "msg-symbol-one")]}, - ), - }, - "Message symbol 'msg-symbol-one' cannot be used for 'W1234' and 'W1235' at the same time.", - ), - ( - { - "W1234": ( - "message one", - "msg-symbol-one", - "msg description", - {"old_names": [("W1230", "old-symbol-one")]}, - ), - "W1235": ( - "message two", - "msg-symbol-two", - "msg description", - {"old_names": [("W1231", "old-symbol-one")]}, - ), - }, - "Message symbol 'old-symbol-one' cannot be used for 'W1230' and 'W1235' at the same time.", - ), - ], -) -def test_register_error(store, messages, expected): - class Checker(object): - name = "checker" - msgs = messages - - with pytest.raises(InvalidMessageError) as cm: - store.register_messages_from_checker(Checker()) - assert str(cm.value) == expected - - -def test_register_error_new_id_duplicate_of_new(store): - class CheckerOne(object): - name = "checker_one" - msgs = {"W1234": ("message one", "msg-symbol-one", "msg description.")} - - class CheckerTwo(object): - name = "checker_two" - msgs = {"W1234": ("message two", "msg-symbol-two", "another msg description.")} - - store.register_messages_from_checker(CheckerOne()) - test_register_error( - store, - {"W1234": ("message two", "msg-symbol-two", "another msg description.")}, - "Message id 'W1234' cannot have both 'msg-symbol-one' and 'msg-symbol-two' as symbolic name.", - ) - - def test_decoding_stream_unknown_encoding(): """decoding_stream should fall back to *some* decoding when given an unknown encoding. From b5faf228561266786800e1a788fe9b01d25c55dc Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Fri, 8 Mar 2019 20:21:00 +0100 Subject: [PATCH 0037/5147] Refactor - Create a unittest file for MessageStore Taken from unittest_lint.py --- pylint/test/message/unittest_message_store.py | 109 +++++++++++++++ pylint/test/unittest_lint.py | 129 ++---------------- 2 files changed, 124 insertions(+), 114 deletions(-) create mode 100644 pylint/test/message/unittest_message_store.py diff --git a/pylint/test/message/unittest_message_store.py b/pylint/test/message/unittest_message_store.py new file mode 100644 index 0000000000..09ff6d4c8f --- /dev/null +++ b/pylint/test/message/unittest_message_store.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +# 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 contextlib import redirect_stdout +from io import StringIO + +import pytest + +from pylint.exceptions import InvalidMessageError, UnknownMessageError +from pylint.message import MessageDefinition, MessagesStore + + +@pytest.fixture +def store(): + store = MessagesStore() + + class Checker(object): + name = "achecker" + msgs = { + "W1234": ( + "message", + "msg-symbol", + "msg description.", + {"old_names": [("W0001", "old-symbol")]}, + ), + "E1234": ( + "Duplicate keyword argument %r in %s call", + "duplicate-keyword-arg", + "Used when a function call passes the same keyword argument multiple times.", + {"maxversion": (2, 6)}, + ), + } + + store.register_messages_from_checker(Checker()) + return store + + +class TestMessagesStore(object): + def _compare_messages(self, desc, msg, checkerref=False): + assert desc == msg.format_help(checkerref=checkerref) + + def test_check_message_id(self, store): + assert isinstance(store.get_message_definitions("W1234")[0], MessageDefinition) + with pytest.raises(UnknownMessageError): + store.get_message_definitions("YB12") + + def test_message_help(self, store): + message_definition = store.get_message_definitions("W1234")[0] + self._compare_messages( + """:msg-symbol (W1234): *message* + msg description. This message belongs to the achecker checker.""", + message_definition, + checkerref=True, + ) + self._compare_messages( + """:msg-symbol (W1234): *message* + msg description.""", + message_definition, + checkerref=False, + ) + + def test_message_help_minmax(self, store): + # build the message manually to be python version independent + message_definition = store.get_message_definitions("E1234")[0] + self._compare_messages( + """:duplicate-keyword-arg (E1234): *Duplicate keyword argument %r in %s call* + Used when a function call passes the same keyword argument multiple times. + This message belongs to the achecker checker. It can't be emitted when using + Python >= 2.6.""", + message_definition, + checkerref=True, + ) + self._compare_messages( + """:duplicate-keyword-arg (E1234): *Duplicate keyword argument %r in %s call* + Used when a function call passes the same keyword argument multiple times. + This message can't be emitted when using Python >= 2.6.""", + message_definition, + checkerref=False, + ) + + def test_list_messages(self, store): + output = StringIO() + with redirect_stdout(output): + store.list_messages() + # cursory examination of the output: we're mostly testing it completes + assert ":msg-symbol (W1234): *message*" in output.getvalue() + + def test_add_renamed_message(self, store): + store.add_renamed_message("W1234", "old-bad-name", "msg-symbol") + assert "msg-symbol" == store.get_message_definitions("W1234")[0].symbol + assert "msg-symbol" == store.get_message_definitions("old-bad-name")[0].symbol + + def test_add_renamed_message_invalid(self, store): + # conflicting message ID + with pytest.raises(InvalidMessageError) as cm: + store.add_renamed_message( + "W1234", "old-msg-symbol", "duplicate-keyword-arg" + ) + expected = ( + "Message id 'W1234' cannot have both 'msg-symbol' and 'old-msg-symbol' " + "as symbolic name." + ) + assert str(cm.value) == expected + + def test_renamed_message_register(self, store): + assert "msg-symbol" == store.get_message_definitions("W0001")[0].symbol + assert "msg-symbol" == store.get_message_definitions("old-symbol")[0].symbol diff --git a/pylint/test/unittest_lint.py b/pylint/test/unittest_lint.py index efd9d07a97..3e58558344 100644 --- a/pylint/test/unittest_lint.py +++ b/pylint/test/unittest_lint.py @@ -26,35 +26,33 @@ # 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 contextlib import contextmanager, redirect_stdout -import sys import os import re +import sys import tempfile -from shutil import rmtree -from os import getcwd, chdir -from os.path import join, basename, dirname, isdir, abspath, sep +from contextlib import contextmanager, redirect_stdout from importlib import reload from io import StringIO +from os import chdir, getcwd +from os.path import abspath, basename, dirname, isdir, join, sep +from shutil import rmtree + +import pytest -from pylint import config, lint -from pylint.lint import PyLinter, Run, preprocess_options, ArgumentPreprocessingError +import pylint.testutils as testutils +from pylint import checkers, config, exceptions, interfaces, lint +from pylint.checkers.utils import check_messages +from pylint.exceptions import InvalidMessageError, UnknownMessageError +from pylint.lint import ArgumentPreprocessingError, PyLinter, Run, preprocess_options from pylint.message import ( + MSG_STATE_CONFIDENCE, MSG_STATE_SCOPE_CONFIG, MSG_STATE_SCOPE_MODULE, - MSG_STATE_CONFIDENCE, - MessagesStore, MessageDefinition, + MessagesStore, ) -from pylint.utils import FileState, tokenize_module -from pylint.exceptions import InvalidMessageError, UnknownMessageError -import pylint.testutils as testutils from pylint.reporters import text -from pylint import checkers -from pylint.checkers.utils import check_messages -from pylint import exceptions -from pylint import interfaces -import pytest +from pylint.utils import FileState, tokenize_module if os.name == "java": if os._name == "nt": @@ -725,103 +723,6 @@ def test_error_unexpected_value(self): ) -@pytest.fixture -def store(): - store = MessagesStore() - - class Checker(object): - name = "achecker" - msgs = { - "W1234": ( - "message", - "msg-symbol", - "msg description.", - {"old_names": [("W0001", "old-symbol")]}, - ), - "E1234": ( - "Duplicate keyword argument %r in %s call", - "duplicate-keyword-arg", - "Used when a function call passes the same keyword argument multiple times.", - {"maxversion": (2, 6)}, - ), - } - - store.register_messages_from_checker(Checker()) - return store - - -class TestMessagesStore(object): - def _compare_messages(self, desc, msg, checkerref=False): - assert desc == msg.format_help(checkerref=checkerref) - - def test_check_message_id(self, store): - assert isinstance(store.get_message_definitions("W1234")[0], MessageDefinition) - with pytest.raises(UnknownMessageError): - store.get_message_definitions("YB12") - - def test_message_help(self, store): - message_definition = store.get_message_definitions("W1234")[0] - self._compare_messages( - """:msg-symbol (W1234): *message* - msg description. This message belongs to the achecker checker.""", - message_definition, - checkerref=True, - ) - self._compare_messages( - """:msg-symbol (W1234): *message* - msg description.""", - message_definition, - checkerref=False, - ) - - def test_message_help_minmax(self, store): - # build the message manually to be python version independent - message_definition = store.get_message_definitions("E1234")[0] - self._compare_messages( - """:duplicate-keyword-arg (E1234): *Duplicate keyword argument %r in %s call* - Used when a function call passes the same keyword argument multiple times. - This message belongs to the achecker checker. It can't be emitted when using - Python >= 2.6.""", - message_definition, - checkerref=True, - ) - self._compare_messages( - """:duplicate-keyword-arg (E1234): *Duplicate keyword argument %r in %s call* - Used when a function call passes the same keyword argument multiple times. - This message can't be emitted when using Python >= 2.6.""", - message_definition, - checkerref=False, - ) - - def test_list_messages(self, store): - output = StringIO() - with redirect_stdout(output): - store.list_messages() - # cursory examination of the output: we're mostly testing it completes - assert ":msg-symbol (W1234): *message*" in output.getvalue() - - def test_add_renamed_message(self, store): - store.add_renamed_message("W1234", "old-bad-name", "msg-symbol") - assert "msg-symbol" == store.get_message_definitions("W1234")[0].symbol - assert "msg-symbol" == store.get_message_definitions("old-bad-name")[0].symbol - - def test_add_renamed_message_invalid(self, store): - # conflicting message ID - with pytest.raises(InvalidMessageError) as cm: - store.add_renamed_message( - "W1234", "old-msg-symbol", "duplicate-keyword-arg" - ) - expected = ( - "Message id 'W1234' cannot have both 'msg-symbol' and 'old-msg-symbol' " - "as symbolic name." - ) - assert str(cm.value) == expected - - def test_renamed_message_register(self, store): - assert "msg-symbol" == store.get_message_definitions("W0001")[0].symbol - assert "msg-symbol" == store.get_message_definitions("old-symbol")[0].symbol - - def test_custom_should_analyze_file(): """Check that we can write custom should_analyze_file that work even for arguments. From b2a77e465cf10f7f1122262131aa4071faf74804 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Fri, 8 Mar 2019 20:29:06 +0100 Subject: [PATCH 0038/5147] Refactor - Rename PylintASTWalker to ASTWalker For obvious reasons. See review here : https://github.com/PyCQA/pylint/pull/2654#discussion_r263843101 --- pylint/lint.py | 10 ++-------- pylint/test/utils/unittest_utils.py | 8 ++++---- pylint/testutils.py | 4 ++-- pylint/utils/__init__.py | 2 +- pylint/utils/{pylint_ast_walker.py => ast_walker.py} | 2 +- 5 files changed, 10 insertions(+), 16 deletions(-) rename pylint/utils/{pylint_ast_walker.py => ast_walker.py} (99%) diff --git a/pylint/lint.py b/pylint/lint.py index 5aa3bd0c78..c012c0b296 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -82,13 +82,7 @@ from pylint import interfaces from pylint import reporters from pylint.message import MessagesStore, Message, MSG_TYPES, MessagesHandlerMixIn -from pylint.utils import ( - FileState, - PyLintASTWalker, - ReportsHandlerMixIn, - OPTION_RGX, - utils, -) +from pylint.utils import FileState, ASTWalker, ReportsHandlerMixIn, OPTION_RGX, utils from pylint import exceptions from pylint import config from pylint.__pkginfo__ import version @@ -1072,7 +1066,7 @@ def _parallel_check(self, files_or_modules): checker.stats = self.stats def _do_check(self, files_or_modules): - walker = PyLintASTWalker(self) + walker = ASTWalker(self) _checkers = self.prepare_checkers() tokencheckers = [ c diff --git a/pylint/test/utils/unittest_utils.py b/pylint/test/utils/unittest_utils.py index e593c3aec1..6ad932dc61 100644 --- a/pylint/test/utils/unittest_utils.py +++ b/pylint/test/utils/unittest_utils.py @@ -22,11 +22,11 @@ import astroid -from pylint.utils import utils, PyLintASTWalker +from pylint.utils import utils, ASTWalker from pylint.checkers.utils import check_messages, get_node_last_lineno -class TestPyLintASTWalker(object): +class TestASTWalker(object): class MockLinter(object): def __init__(self, msgs): self._msgs = msgs @@ -58,7 +58,7 @@ def test_check_messages(self): linter = self.MockLinter( {"first-message": True, "second-message": False, "third-message": True} ) - walker = PyLintASTWalker(linter) + walker = ASTWalker(linter) checker = self.Checker() walker.add_checker(checker) walker.walk(astroid.parse("x = func()")) @@ -74,7 +74,7 @@ def visit_assname(self, node): self.called = True linter = self.MockLinter({"first-message": True}) - walker = PyLintASTWalker(linter) + walker = ASTWalker(linter) checker = Checker() walker.add_checker(checker) with warnings.catch_warnings(record=True): diff --git a/pylint/testutils.py b/pylint/testutils.py index f22b897e8d..17ded416a8 100644 --- a/pylint/testutils.py +++ b/pylint/testutils.py @@ -37,7 +37,7 @@ import astroid from pylint import checkers -from pylint.utils import PyLintASTWalker +from pylint.utils import ASTWalker from pylint.reporters import BaseReporter from pylint.interfaces import IReporter from pylint.lint import PyLinter @@ -254,7 +254,7 @@ def assertAddsMessages(self, *messages): def walk(self, node): """recursive walk on the given node""" - walker = PyLintASTWalker(linter) + walker = ASTWalker(linter) walker.add_checker(self.checker) walker.walk(node) diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index 0a4f6fd3c2..1b29540782 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -44,7 +44,7 @@ from pylint.utils.constants import OPTION_RGX, PY_EXTS from pylint.utils.file_state import FileState from pylint.utils.normalize_text import normalize_text -from pylint.utils.pylint_ast_walker import PyLintASTWalker +from pylint.utils.ast_walker import ASTWalker from pylint.utils.reports_handler_mix_in import ReportsHandlerMixIn from pylint.utils.utils import ( _basename_in_blacklist_re, diff --git a/pylint/utils/pylint_ast_walker.py b/pylint/utils/ast_walker.py similarity index 99% rename from pylint/utils/pylint_ast_walker.py rename to pylint/utils/ast_walker.py index e758422a61..1350d61f22 100644 --- a/pylint/utils/pylint_ast_walker.py +++ b/pylint/utils/ast_walker.py @@ -8,7 +8,7 @@ from astroid import nodes -class PyLintASTWalker: +class ASTWalker: def __init__(self, linter): # callbacks per node types self.nbstatements = 0 From 5073ebd9fb907ce258ec9e3e15f092c43132f25e Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Fri, 8 Mar 2019 20:36:25 +0100 Subject: [PATCH 0039/5147] Refactor - Rename _ini_format_section directly to format_section --- pylint/utils/utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index 1eb279346d..fb8e1ef10a 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -315,7 +315,7 @@ def _format_option_value(optdict, value): return value -def _ini_format_section(stream, section, options, doc=None): +def format_section(stream, section, options, doc=None): """format an options section using the INI format""" if doc: print(_comment(doc), file=stream) @@ -344,6 +344,3 @@ def _ini_format(stream, options): # remove trailing ',' from last element of the list value = value[:-1] print("%s=%s" % (optname, value), file=stream) - - -format_section = _ini_format_section From 00726c8a021df2845b20ae9ea7cb13aabb81438c Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 11 Mar 2019 16:11:52 +0100 Subject: [PATCH 0040/5147] ``old-division`` is not emitted for non-Const nodes. Close #2808 --- ChangeLog | 4 ++++ pylint/checkers/python3.py | 7 +++++++ pylint/test/unittest_checker_python3.py | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/ChangeLog b/ChangeLog index 45526785e0..346f1d67de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* ``old-division`` is not emitted for non-Const nodes. + + Close #2808 + * Added method arguments to the dot writer for pyreverse. Close #2139 diff --git a/pylint/checkers/python3.py b/pylint/checkers/python3.py index c8e1dd92de..c84f2b997a 100644 --- a/pylint/checkers/python3.py +++ b/pylint/checkers/python3.py @@ -1069,6 +1069,13 @@ def visit_classdef(self, node): 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 a numeric one, bail out. + if inferred and not ( + isinstance(inferred, astroid.Const) + and isinstance(inferred.value, (int, float)) + ): + break if isinstance(arg, astroid.Const) and isinstance(arg.value, float): break else: diff --git a/pylint/test/unittest_checker_python3.py b/pylint/test/unittest_checker_python3.py index 8354ed82e0..bc4c346b70 100644 --- a/pylint/test/unittest_checker_python3.py +++ b/pylint/test/unittest_checker_python3.py @@ -480,6 +480,24 @@ def test_division_by_float(self): for node in (left_node, right_node): self.checker.visit_binop(node) + def test_division_different_types(self): + nodes = astroid.extract_node( + """ + class A: + pass + A / 2 #@ + A() / 2 #@ + "a" / "a" #@ + class Path: + def __div__(self, other): + return 42 + Path() / 24 #@ + """ + ) + for node in nodes: + with self.assertNoMessages(): + self.checker.visit_binop(node) + def test_dict_iter_method(self): for meth in ("keys", "values", "items"): node = astroid.extract_node("x.iter%s() #@" % meth) From 40e8ef97b13c62e36308185b30c82c808b177c30 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 11 Mar 2019 16:26:43 +0100 Subject: [PATCH 0041/5147] ``assigning-non-slot`` not emitted for classes with unknown base classes. It's possible to lint classes that don't have known bases (e.g. one of them might come from a C extension), in which case we cannot make a lot of assumptions about the class layout with respect to `__slots__`. As such, it's best to ignore these classes from this check. Close #2807 --- ChangeLog | 4 ++ pylint/checkers/classes.py | 72 ++++++++++---------- pylint/test/functional/assigning_non_slot.py | 8 +++ 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/ChangeLog b/ChangeLog index 346f1d67de..13aa84be79 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* ``assigning-non-slot`` not emitted for classes with unknown base classes. + + Close #2807 + * ``old-division`` is not emitted for non-Const nodes. Close #2808 diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 273cc47d14..543b27ecf7 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -1147,43 +1147,45 @@ def _check_in_slots(self, node): """ Check that the given AssignAttr node is defined in the class slots. """ - infered = safe_infer(node.expr) - if infered and isinstance(infered, astroid.Instance): - klass = infered._proxied - if "__slots__" not in klass.locals or not klass.newstyle: - return + inferred = safe_infer(node.expr) + if not isinstance(inferred, astroid.Instance): + 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 + klass = inferred._proxied + if not has_known_bases(klass): + return + if "__slots__" not in klass.locals or not klass.newstyle: + 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 - ) + 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" diff --git a/pylint/test/functional/assigning_non_slot.py b/pylint/test/functional/assigning_non_slot.py index a57a8b3b40..894f172997 100644 --- a/pylint/test/functional/assigning_non_slot.py +++ b/pylint/test/functional/assigning_non_slot.py @@ -148,3 +148,11 @@ class ClassReassingingInvalidLayoutClass(object): def release(self): self.__class__ = ClassWithSlots # [assigning-non-slot] + + +# pylint: disable=attribute-defined-outside-init +class ClassHavingUnknownAncestors(Unknown): + __slots__ = ['yo'] + + def test(self): + self.not_yo = 42 From 60f39390d2079ea51b57d396276c0a06558f1dc9 Mon Sep 17 00:00:00 2001 From: bluesheeptoken Date: Tue, 19 Mar 2019 08:13:53 +0000 Subject: [PATCH 0042/5147] Modify range-builtin-not-iterating message (#2810) --- CONTRIBUTORS.txt | 1 + pylint/checkers/python3.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index e01a3d2cf7..c9890157df 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -278,3 +278,4 @@ contributors: * Goudcode: contributor +* Bluesheeptoken: contributor diff --git a/pylint/checkers/python3.py b/pylint/checkers/python3.py index c84f2b997a..d577c918c6 100644 --- a/pylint/checkers/python3.py +++ b/pylint/checkers/python3.py @@ -438,7 +438,7 @@ class Python3Checker(checkers.BaseChecker): "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 an iterator in Python 3)", + "context (returns a range in Python 3)", ), "W1639": ( "filter built-in referenced when not iterating", From 095928cceaef476f2e76a64a63cf215ffa782f5c Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Tue, 19 Mar 2019 09:22:22 +0100 Subject: [PATCH 0043/5147] Skip `if` expressions from f-strings for the `check_elif` checker Close #2816 --- ChangeLog | 4 ++++ pylint/extensions/check_elif.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 13aa84be79..0fe06f1fcd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -37,6 +37,10 @@ Release date: TBA * Relicense logo material under the CC BY-SA 4.0 license. +* Skip `if` expressions from f-strings for the `check_elif` checker + + Close #2816 + What's New in Pylint 2.3.0? =========================== diff --git a/pylint/extensions/check_elif.py b/pylint/extensions/check_elif.py index a259361ec4..9ef0b3fa33 100644 --- a/pylint/extensions/check_elif.py +++ b/pylint/extensions/check_elif.py @@ -48,7 +48,9 @@ def process_tokens(self, tokens): def leave_module(self, _): self._init() - def visit_ifexp(self, _): + def visit_ifexp(self, node): + if isinstance(node.parent, astroid.FormattedValue): + return self._if_counter += 1 def visit_comprehension(self, node): From 7b5e0e797709afcacd440cab4b326973d3397f3f Mon Sep 17 00:00:00 2001 From: Jim Robertson Date: Tue, 19 Mar 2019 04:11:59 -0500 Subject: [PATCH 0044/5147] Fix misattribution of `unused import` (#2672) Fixed issue where import statements with multiple import arguments wouldn't flag the correct missing package. --- pylint/checkers/variables.py | 14 +++++++++++--- pylint/test/functional/unused_import.py | 6 +++++- pylint/test/functional/unused_import.txt | 3 ++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 694be8e5e2..85de92aaa1 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -213,16 +213,24 @@ def _fix_dot_imports(not_consumed): continue for imports in stmt.names: second_name = None - if imports[0] == "*": + 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: - if imports[0].find(".") > -1 or name in imports: + 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 = imports[0] + 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) diff --git a/pylint/test/functional/unused_import.py b/pylint/test/functional/unused_import.py index e551db5d41..4f265277a1 100644 --- a/pylint/test/functional/unused_import.py +++ b/pylint/test/functional/unused_import.py @@ -1,5 +1,5 @@ """unused import""" -# pylint: disable=undefined-all-variable, import-error, no-absolute-import, too-few-public-methods, missing-docstring,wrong-import-position, useless-object-inheritance +# pylint: disable=undefined-all-variable, import-error, no-absolute-import, too-few-public-methods, missing-docstring,wrong-import-position, useless-object-inheritance, multiple-imports import xml.etree # [unused-import] import xml.sax # [unused-import] import os.path as test # [unused-import] @@ -7,6 +7,7 @@ from sys import flags # [unused-import] # +1:[unused-import,unused-import] from collections import deque, OrderedDict, Counter +import re, html.parser # [unused-import] DATA = Counter() from fake import SomeName, SomeOtherName # [unused-import] @@ -33,3 +34,6 @@ def get_ordered_dict() -> 'collections.OrderedDict': def get_itertools_obj() -> 'itertools.count': return [] + +def use_html_parser() -> 'html.parser.HTMLParser': + return html.parser.HTMLParser diff --git a/pylint/test/functional/unused_import.txt b/pylint/test/functional/unused_import.txt index 9d6497f960..aa3c077262 100644 --- a/pylint/test/functional/unused_import.txt +++ b/pylint/test/functional/unused_import.txt @@ -5,4 +5,5 @@ unused-import:6::Unused argv imported from sys as test2 unused-import:7::Unused flags imported from sys unused-import:9::Unused OrderedDict imported from collections unused-import:9::Unused deque imported from collections -unused-import:12::Unused SomeOtherName imported from fake +unused-import:10::Unused import re +unused-import:13::Unused SomeOtherName imported from fake From 4714a65529a0f03cb77e3938503c131d62fdc9e0 Mon Sep 17 00:00:00 2001 From: Paul Renvoise Date: Tue, 19 Mar 2019 10:16:07 +0100 Subject: [PATCH 0045/5147] Make ``len-as-condition`` only fire when a ``len(x)`` call is made without an explicit comparison This commit reduce the scope of `len-as-condition` to only care when a `len(SEQUENCE)` call is made without an explicit comparison, as stated in PEP8. --- CONTRIBUTORS.txt | 2 + ChangeLog | 9 +- doc/whatsnew/2.4.rst | 26 ++++ pylint/checkers/refactoring.py | 212 +++++++++----------------- pylint/test/functional/len_checks.py | 88 +++++++---- pylint/test/functional/len_checks.txt | 29 ++-- 6 files changed, 181 insertions(+), 185 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index c9890157df..325a5394c2 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -278,4 +278,6 @@ contributors: * Goudcode: contributor +* Paul Renvoise : contributor + * Bluesheeptoken: contributor diff --git a/ChangeLog b/ChangeLog index 0fe06f1fcd..7fe8b8b83a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,14 @@ What's New in Pylint 2.4.0? Release date: TBA +* ``len-as-condition`` now only fires when a ``len(x)`` call is made without an explicit comparison + + The message and description accompanying this checker has been changed + reflect this new behavior, by explicitly asking to either rely on the + fact that empty sequence are false or to compare the length with a scalar. + + Close #2684 + * ``assigning-non-slot`` not emitted for classes with unknown base classes. Close #2807 @@ -30,7 +38,6 @@ Release date: TBA This check is emitted when ``pylint`` finds a class variable that conflicts with a slot name, which would raise a ``ValueError`` at runtime. - * Fix issue with pylint name in output of python -m pylint --version Close #2764 diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index 2245d372ce..4f8a98e8c7 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -28,6 +28,32 @@ New checkers Other Changes ============= +* ``len-as-condition`` now only fires when a ``len(x)`` call is made without an explicit comparison. + + The message and description accompanying this checker has been changed + reflect this new behavior, by explicitly asking to either rely on the + fact that empty sequence are false or to compare the length with a scalar. + + OK:: + + if len(x) == 0: + pass + + while not len(x) == 0: + pass + + assert len(x) > 5, message + + KO:: + + if not len(x): + pass + + while len(x) and other_cond: + pass + + assert len(x), message + * A file is now read from stdin if the ``--from-stdin`` flag is used on the command line. In addition to the ``--from-stdin`` flag a (single) file name needs to be specified on the command line, which is needed for the diff --git a/pylint/checkers/refactoring.py b/pylint/checkers/refactoring.py index 1777e69739..6a349c0677 100644 --- a/pylint/checkers/refactoring.py +++ b/pylint/checkers/refactoring.py @@ -47,6 +47,69 @@ def _if_statement_is_always_returning(if_node, returning_node_class): 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 @@ -355,7 +418,7 @@ def process_tokens(self, tokens): # 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): + 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]) @@ -1239,28 +1302,6 @@ def visit_unaryop(self, node): ) -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 _has_constant_value(node, value): - return isinstance(node, astroid.Const) and node.value == value - - -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)) - - class LenChecker(checkers.BaseChecker): """Checks for incorrect usage of len() inside conditions. Pep8 states: @@ -1275,11 +1316,12 @@ class LenChecker(checkers.BaseChecker): Problems detected: * if len(sequence): * if not len(sequence): - * if len(sequence) == 0: - * if len(sequence) != 0: - * if len(sequence) > 0: - * if len(sequence) < 1: - * if len(sequence) <= 0: + * elif len(sequence): + * elif not len(sequence): + * while len(sequence): + * while not len(sequence): + * assert len(sequence): + * assert not len(sequence): """ __implements__ = (interfaces.IAstroidChecker,) @@ -1288,12 +1330,13 @@ class LenChecker(checkers.BaseChecker): name = "refactoring" msgs = { "C1801": ( - "Do not use `len(SEQUENCE)` to determine if a sequence is empty", + "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 inside " - "a condition to determine if a sequence is empty. Instead of " - "comparing the length to 0, rely on the fact that empty sequences " - "are false.", + "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.", ) } @@ -1332,109 +1375,6 @@ def visit_unaryop(self, node): ): self.add_message("len-as-condition", node=node) - @utils.check_messages("len-as-condition") - def visit_compare(self, node): - # compare nodes are trickier because the len(S) expression - # may be somewhere in the middle of the node - - # note: astroid.Compare has the left most operand in node.left - # while the rest are a list of tuples in node.ops - # the format of the tuple is ('compare operator sign', node) - # here we squash everything into `ops` to make it easier for processing later - ops = [("", node.left)] - ops.extend(node.ops) - ops = list(itertools.chain(*ops)) - - for ops_idx in range(len(ops) - 2): - op_1 = ops[ops_idx] - op_2 = ops[ops_idx + 1] - op_3 = ops[ops_idx + 2] - error_detected = False - - # 0 ?? len() - if ( - _is_constant_zero(op_1) - and op_2 in ["==", "!=", "<", ">="] - and _is_len_call(op_3) - ): - error_detected = True - # len() ?? 0 - elif ( - _is_len_call(op_1) - and op_2 in ["==", "!=", ">", "<="] - and _is_constant_zero(op_3) - ): - error_detected = True - elif ( - _has_constant_value(op_1, value=1) - and op_2 == ">" - and _is_len_call(op_3) - ): - error_detected = True - elif ( - _is_len_call(op_1) - and op_2 == "<" - and _has_constant_value(op_3, value=1) - ): - error_detected = True - - if error_detected: - parent = node.parent - # traverse the AST to figure out if this comparison was part of - # a test condition - while parent and not _node_is_test_condition(parent): - parent = parent.parent - - # report only if this len() comparison is part of a test condition - # for example: return len() > 0 should not report anything - if _node_is_test_condition(parent): - self.add_message("len-as-condition", node=node) - - -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 - def register(linter): """Required method to auto register this checker.""" diff --git a/pylint/test/functional/len_checks.py b/pylint/test/functional/len_checks.py index 8b35a22f02..e8e61afadb 100644 --- a/pylint/test/functional/len_checks.py +++ b/pylint/test/functional/len_checks.py @@ -1,77 +1,101 @@ # pylint: disable=too-few-public-methods,import-error, no-absolute-import,missing-docstring, misplaced-comparison-constant # pylint: disable=useless-super-delegation,wrong-import-position,invalid-name, wrong-import-order -if len('TEST'): # [len-as-condition] +if len('TEST'): # [len-as-condition] pass -while not len('TEST'): # [len-as-condition] +if not len('TEST'): # [len-as-condition] pass -assert len('TEST') > 0 # [len-as-condition] +z = False +if z and len(['T', 'E', 'S', 'T']): # [len-as-condition] + pass -x = 1 if len('TEST') != 0 else 2 # [len-as-condition] +if True or len('TEST'): # [len-as-condition] + pass -if len('TEST') == 0: # [len-as-condition] +if len('TEST') == 0: # Should be fine pass -if True and len('TEST') == 0: # [len-as-condition] +if len('TEST') < 1: # Should be fine pass -if 0 == len('TEST') < 10: # [len-as-condition] +if len('TEST') <= 0: # Should be fine pass -if 0 < 1 <= len('TEST') < 10: # Should be fine +if 1 > len('TEST'): # Should be fine pass -if 10 > len('TEST') != 0: # [len-as-condition] +if 0 >= len('TEST'): # Should be fine pass -z = False -if z and len(['T', 'E', 'S', 'T']): # [len-as-condition] +if z and len('TEST') == 0: # Should be fine pass -if 10 > len('TEST') > 1 > 0: +if 0 == len('TEST') < 10: # Should be fine pass -f_o_o = len('TEST') or 42 # Should be fine +if 0 < 1 <= len('TEST') < 10: # Should be fine + pass -a = x and len(x) # Should be fine +if 10 > len('TEST') != 0: # Should be fine + pass + +if 10 > len('TEST') > 1 > 0: # Should be fine + pass if 0 <= len('TEST') < 100: # Should be fine pass -if z or 10 > len('TEST') != 0: # [len-as-condition] +if z or 10 > len('TEST') != 0: # Should be fine + pass + +if z: + pass +elif len('TEST'): # [len-as-condition] + pass + +if z: + pass +elif not len('TEST'): # [len-as-condition] + pass + +while len('TEST'): # [len-as-condition] + pass + +while not len('TEST'): # [len-as-condition] pass +while z and len('TEST'): # [len-as-condition] + pass + +while not len('TEST') and z: # [len-as-condition] + pass + +assert len('TEST') > 0 # Should be fine + +x = 1 if len('TEST') != 0 else 2 # Should be fine + +f_o_o = len('TEST') or 42 # Should be fine + +a = x and len(x) # Should be fine + def some_func(): return len('TEST') > 0 # Should be fine - def github_issue_1325(): l = [1, 2, 3] - length = len(l) if l else 0 + length = len(l) if l else 0 # Should be fine return length - def github_issue_1331(*args): - assert False, len(args) - + assert False, len(args) # Should be fine def github_issue_1331_v2(*args): assert len(args), args # [len-as-condition] - def github_issue_1331_v3(*args): assert len(args) or z, args # [len-as-condition] -if len('TEST') < 1: # [len-as-condition] - pass - -if len('TEST') <= 0: # [len-as-condition] - pass - -if 1 > len('TEST'): # [len-as-condition] - pass - -if 0 >= len('TEST'): # [len-as-condition] - pass +def github_issue_1331_v4(*args): + assert z and len(args), args # [len-as-condition] diff --git a/pylint/test/functional/len_checks.txt b/pylint/test/functional/len_checks.txt index a3ee6787fd..9dc2969f6a 100644 --- a/pylint/test/functional/len_checks.txt +++ b/pylint/test/functional/len_checks.txt @@ -1,16 +1,13 @@ -len-as-condition:4::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:7::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:10::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:12::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:14::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:17::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:20::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:26::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:30::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:43::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:61:github_issue_1331_v2:Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:65:github_issue_1331_v3:Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:67::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:70::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:73::Do not use `len(SEQUENCE)` to determine if a sequence is empty -len-as-condition:76::Do not use `len(SEQUENCE)` to determine if a sequence is empty +len-as-condition:4::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:7::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:11::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:14::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:55::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:60::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:63::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:66::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:69::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:72::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:95:github_issue_1331_v2:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:98:github_issue_1331_v3:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty +len-as-condition:101:github_issue_1331_v4:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty From 383c2a9dd4409a3bfeeb206722bb78045e976143 Mon Sep 17 00:00:00 2001 From: Michael Scott Cuthbert Date: Sun, 3 Feb 2019 14:06:27 -0500 Subject: [PATCH 0046/5147] Add reference to R0916 to max-bool-expr --- CONTRIBUTORS.txt | 3 +++ examples/pylintrc | 2 +- pylint/checkers/design_analysis.py | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 325a5394c2..18ae9a83c0 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -281,3 +281,6 @@ contributors: * Paul Renvoise : contributor * Bluesheeptoken: contributor + +* Michael Scott Cuthbert: contributor + diff --git a/examples/pylintrc b/examples/pylintrc index 872166ee91..c2a1558ce7 100644 --- a/examples/pylintrc +++ b/examples/pylintrc @@ -485,7 +485,7 @@ max-args=5 # Maximum number of attributes for a class (see R0902). max-attributes=7 -# Maximum number of boolean expressions in an if statement +# Maximum number of boolean expressions in an if statement (see R0916). max-bool-expr=5 # Maximum number of branch for function / method body diff --git a/pylint/checkers/design_analysis.py b/pylint/checkers/design_analysis.py index a0c48b9784..e9f0a04707 100644 --- a/pylint/checkers/design_analysis.py +++ b/pylint/checkers/design_analysis.py @@ -285,7 +285,8 @@ class MisdesignChecker(BaseChecker): "default": 5, "type": "int", "metavar": "", - "help": "Maximum number of boolean expressions in an if " "statement.", + "help": "Maximum number of boolean expressions in an if " + "statement (see R0916).", }, ), ) From 8d2f5d090b839b9a934fc9187da3766828734399 Mon Sep 17 00:00:00 2001 From: hippo91 Date: Tue, 19 Mar 2019 10:45:09 +0100 Subject: [PATCH 0047/5147] Add a new ``missing-parentheses-for-call-in-test`` check This is emitted in case a call to a function is made inside a test but it misses parentheses. It is a more specialised form of `using-constant-test`, but tailored specifically to callables. Close #2658 --- ChangeLog | 4 + doc/whatsnew/2.3.rst | 1 - doc/whatsnew/2.4.rst | 4 + pylint/checkers/base.py | 40 ++++++-- .../missing_parentheses_for_call_in_test.py | 95 +++++++++++++++++++ .../missing_parentheses_for_call_in_test.txt | 15 +++ pylint/test/functional/using_constant_test.py | 17 ++-- .../test/functional/using_constant_test.txt | 36 +++---- 8 files changed, 176 insertions(+), 36 deletions(-) create mode 100644 pylint/test/functional/missing_parentheses_for_call_in_test.py create mode 100644 pylint/test/functional/missing_parentheses_for_call_in_test.txt diff --git a/ChangeLog b/ChangeLog index 7fe8b8b83a..095f040f41 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Added a new check that warns the user if a function call is used inside a test but parentheses are missing. + + Close #2658 + * ``len-as-condition`` now only fires when a ``len(x)`` call is made without an explicit comparison The message and description accompanying this checker has been changed diff --git a/doc/whatsnew/2.3.rst b/doc/whatsnew/2.3.rst index 732618526d..94921887d0 100644 --- a/doc/whatsnew/2.3.rst +++ b/doc/whatsnew/2.3.rst @@ -14,7 +14,6 @@ Summary -- Release highlights New checkers ============ - * We added a new check message ``wrong-exception-operation``. This is emitted when an operation is done against an exception, but the operation is not valid for the exception in question. Usually emitted when having diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index 4f8a98e8c7..45a6b832a0 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -13,6 +13,10 @@ Summary -- Release highlights New checkers ============ +* We added a new check message ``missing-parentheses-for-call-in-test``. + This is emitted in case a call to a function is made inside a test but + it misses parentheses. + * A new check ``class-variable-slots-conflict`` was added. This check is emitted when ``pylint`` finds a class variable that conflicts with a slot diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 043700f024..e2e35f3dd4 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -972,6 +972,12 @@ class BasicChecker(_BasicChecker): "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", + ), "E0111": ( "The first reversed() argument is not a sequence", "bad-reversed-sequence", @@ -1002,15 +1008,15 @@ def open(self): self._tryfinallys = [] self.stats = self.linter.add_stats(module=0, function=0, method=0, class_=0) - @utils.check_messages("using-constant-test") + @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") + @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") + @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: @@ -1031,13 +1037,8 @@ def _check_using_constant_test(self, node, test): structs = (astroid.Dict, astroid.Tuple, astroid.Set) # These nodes are excepted, since they are not constant - # values, requiring a computation to happen. The only type - # of node in this list which doesn't have this property is - # Attribute, which is excepted because the conditional statement - # can be used to verify that the attribute was set inside a class, - # which is definitely a valid use case. + # values, requiring a computation to happen. except_nodes = ( - astroid.Attribute, astroid.Call, astroid.BinOp, astroid.BoolOp, @@ -1049,7 +1050,26 @@ def _check_using_constant_test(self, node, test): if not isinstance(test, except_nodes): inferred = utils.safe_infer(test) - if emit or isinstance(inferred, const_nodes): + 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, _): diff --git a/pylint/test/functional/missing_parentheses_for_call_in_test.py b/pylint/test/functional/missing_parentheses_for_call_in_test.py new file mode 100644 index 0000000000..cd1d76a8df --- /dev/null +++ b/pylint/test/functional/missing_parentheses_for_call_in_test.py @@ -0,0 +1,95 @@ +"""Verify if call to function or method inside tests are missing parentheses.""" +# pylint: disable=using-constant-test, missing-docstring, useless-object-inheritance +# pylint: disable=invalid-name, expression-not-assigned + +import collections + + +def bool_function(): + return True + +def nonbool_function(): + return 42 + + +class Class(object): + + @staticmethod + def bool_method(): + return False + + @staticmethod + def nonbool_method(): + return 42 + + +if collections: + pass + +if bool_function: # [missing-parentheses-for-call-in-test] + pass + +if not bool_function(): + pass + +if nonbool_function: # [missing-parentheses-for-call-in-test] + pass + +if nonbool_function() != 42: + pass + +instance = Class() + +if instance.bool_method: # [missing-parentheses-for-call-in-test] + pass + +if not instance.bool_method(): + pass + +if not instance.nonbool_method: + pass +elif instance.bool_method: # [missing-parentheses-for-call-in-test] + pass + +bool_lambda = lambda: True + +if bool_lambda: # [missing-parentheses-for-call-in-test] + pass + +if not bool_lambda(): + pass + +nonbool_lambda = lambda: 42 + +if nonbool_lambda: # [missing-parentheses-for-call-in-test] + pass + +if not nonbool_lambda(): + pass + +MY_VALUE = 42 if bool_function else -1 # [missing-parentheses-for-call-in-test] +MY_2ND_VALUE = 42 if not bool_function() else -1 +MY_THIRD_VALUE = 42 if bool_lambda else -1 # [missing-parentheses-for-call-in-test] +MY_FOURTH_VALUE = 42 if nonbool_lambda else -1 # [missing-parentheses-for-call-in-test] + +[x for x in range(100) if bool_function] # [missing-parentheses-for-call-in-test] +[x for x in range(100) if bool_lambda] # [missing-parentheses-for-call-in-test] +[x for x in range(100) if not bool_function()] +[x for x in range(100) if not bool_lambda()] +[x for x in range(100) if nonbool_lambda] # [missing-parentheses-for-call-in-test] +[x for x in range(100) if nonbool_function] # [missing-parentheses-for-call-in-test] + +def non_const_node_function(): + return (1, 2, 42) + +if non_const_node_function: # [missing-parentheses-for-call-in-test] + pass + +def yielding_function(): + yield 42 + +if yielding_function: # [missing-parentheses-for-call-in-test] + pass + +if not yielding_function(): + pass diff --git a/pylint/test/functional/missing_parentheses_for_call_in_test.txt b/pylint/test/functional/missing_parentheses_for_call_in_test.txt new file mode 100644 index 0000000000..872c7c3dfa --- /dev/null +++ b/pylint/test/functional/missing_parentheses_for_call_in_test.txt @@ -0,0 +1,15 @@ +missing-parentheses-for-call-in-test:29::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:35::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:43::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:51::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:56::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:64::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:70::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:72::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:73::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:75::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:76::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:79::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:80::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:85::Using a conditional statement with potentially wrong function or method call due to missing parentheses +missing-parentheses-for-call-in-test:91::Using a conditional statement with potentially wrong function or method call due to missing parentheses diff --git a/pylint/test/functional/using_constant_test.py b/pylint/test/functional/using_constant_test.py index 8d3c2da9a1..732164ecdb 100644 --- a/pylint/test/functional/using_constant_test.py +++ b/pylint/test/functional/using_constant_test.py @@ -1,6 +1,7 @@ """Verify if constant tests are used inside if statements.""" # pylint: disable=invalid-name, missing-docstring,too-few-public-methods # pylint: disable=no-init,expression-not-assigned, useless-object-inheritance +# pylint: disable=missing-parentheses-for-call-in-test import collections @@ -78,6 +79,14 @@ def test_comprehensions(): {data: 1 for data in range(100) if abs} # [using-constant-test] +# UnboundMethod / Function +if Class.method: # [using-constant-test] + pass + +# BoundMethod +if instance.method: # [using-constant-test] + pass + # For these, we require to do inference, even though the result can be a # constant value. For some of them, we could determine that the test @@ -88,14 +97,6 @@ def test_comprehensions(): if name: pass -# UnboundMethod / Function -if Class.method: - pass - -# BoundMethod -if instance.method: - pass - if 3 + 4: pass diff --git a/pylint/test/functional/using_constant_test.txt b/pylint/test/functional/using_constant_test.txt index b1c54847b7..e10b10ccae 100644 --- a/pylint/test/functional/using_constant_test.txt +++ b/pylint/test/functional/using_constant_test.txt @@ -1,22 +1,24 @@ -using-constant-test:21::Using a conditional statement with a constant value -using-constant-test:25::Using a conditional statement with a constant value -using-constant-test:28::Using a conditional statement with a constant value -using-constant-test:31::Using a conditional statement with a constant value -using-constant-test:34::Using a conditional statement with a constant value -using-constant-test:37::Using a conditional statement with a constant value -using-constant-test:40::Using a conditional statement with a constant value -using-constant-test:43::Using a conditional statement with a constant value -using-constant-test:46::Using a conditional statement with a constant value -using-constant-test:49::Using a conditional statement with a constant value -using-constant-test:52::Using a conditional statement with a constant value -using-constant-test:55::Using a conditional statement with a constant value -using-constant-test:58::Using a conditional statement with a constant value -using-constant-test:61::Using a conditional statement with a constant value -using-constant-test:66::Using a conditional statement with a constant value -using-constant-test:69::Using a conditional statement with a constant value -using-constant-test:73:test_comprehensions:Using a conditional statement with a constant value +using-constant-test:22::Using a conditional statement with a constant value +using-constant-test:26::Using a conditional statement with a constant value +using-constant-test:29::Using a conditional statement with a constant value +using-constant-test:32::Using a conditional statement with a constant value +using-constant-test:35::Using a conditional statement with a constant value +using-constant-test:38::Using a conditional statement with a constant value +using-constant-test:41::Using a conditional statement with a constant value +using-constant-test:44::Using a conditional statement with a constant value +using-constant-test:47::Using a conditional statement with a constant value +using-constant-test:50::Using a conditional statement with a constant value +using-constant-test:53::Using a conditional statement with a constant value +using-constant-test:56::Using a conditional statement with a constant value +using-constant-test:59::Using a conditional statement with a constant value +using-constant-test:62::Using a conditional statement with a constant value +using-constant-test:67::Using a conditional statement with a constant value +using-constant-test:70::Using a conditional statement with a constant value using-constant-test:74:test_comprehensions:Using a conditional statement with a constant value using-constant-test:75:test_comprehensions:Using a conditional statement with a constant value using-constant-test:76:test_comprehensions:Using a conditional statement with a constant value using-constant-test:77:test_comprehensions:Using a conditional statement with a constant value using-constant-test:78:test_comprehensions:Using a conditional statement with a constant value +using-constant-test:79:test_comprehensions:Using a conditional statement with a constant value +using-constant-test:83::Using a conditional statement with a constant value +using-constant-test:87::Using a conditional statement with a constant value From 2ca40bf161fa7c7c4b28c08062849ea1899d91d0 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 09:05:14 +0100 Subject: [PATCH 0048/5147] Feat - Add isort in the pre-commit configuration This is compatible with the current black configuration. See : https://github.com/ambv/black/issues/127 --- .isort.cfg | 7 +++++++ .pre-commit-config.yaml | 4 ++++ 2 files changed, 11 insertions(+) create mode 100644 .isort.cfg diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000000..08417f7750 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,7 @@ +[settings] +multi_line_output=3 +line_length=88 +known_third_party=astroid, sphinx, isort, pytest, mccabe, six, +include_trailing_comma=True +skip_glob=*/functional/**,*/input/**,*/test/extension/**,*/test/regrtest_data/**,*/test/data/** +project=pylint diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6c750f005a..27ded48b92 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,3 +11,7 @@ repos: hooks: - id: trailing-whitespace - id: end-of-file-fixer +- repo: https://github.com/pre-commit/mirrors-isort + rev: v4.3.13 + hooks: + - id: isort From a422e9750843f3b41a6919b9a5fa2c7d54593394 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 09:07:44 +0100 Subject: [PATCH 0049/5147] Chore - Update black and pre-commit in configuration --- .pre-commit-config.yaml | 4 ++-- tox.ini | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 27ded48b92..1e553c91cf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,13 @@ repos: - repo: https://github.com/ambv/black - rev: 18.6b4 + rev: 18.9b0 hooks: - id: black args: [--safe, --quiet] exclude: functional|input|test/extension|test/regrtest_data|test/data python_version: python3.6 - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v1.2.3 + rev: v2.1.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer diff --git a/tox.ini b/tox.ini index d847ac7673..c6ee64354a 100644 --- a/tox.ini +++ b/tox.ini @@ -12,7 +12,7 @@ commands = pylint -rn --rcfile={toxinidir}/pylintrc --load-plugins=pylint.extens [testenv:formatting] basepython = python3 -deps = black==18.6b4 +deps = black==18.9b0 commands = black --check --exclude "functional|input|test/extension|test/regrtest_data|test/data" pylint changedir = {toxinidir} From 5ab140ac3b3dda6f32e0f73aeec09abf015ea30f Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 11:22:36 +0100 Subject: [PATCH 0050/5147] Style - Apply isort on all apllicable files --- bin/epylint | 1 + bin/pylint | 1 + bin/pyreverse | 1 + bin/symilar | 1 + doc/conf.py | 15 ++++----- doc/exts/pylint_extensions.py | 1 - doc/exts/pylint_features.py | 1 + examples/custom.py | 2 +- examples/custom_raw.py | 4 +-- pylint/checkers/__init__.py | 2 +- pylint/checkers/async.py | 7 ++--- pylint/checkers/base.py | 9 ++---- pylint/checkers/classes.py | 31 +++++++++---------- pylint/checkers/design_analysis.py | 10 +++--- pylint/checkers/exceptions.py | 3 +- pylint/checkers/format.py | 4 +-- pylint/checkers/imports.py | 18 +++++------ pylint/checkers/logging.py | 4 +-- pylint/checkers/misc.py | 7 ++--- pylint/checkers/newstyle.py | 4 +-- pylint/checkers/python3.py | 9 +++--- pylint/checkers/raw_metrics.py | 4 +-- pylint/checkers/refactoring.py | 7 ++--- pylint/checkers/similar.py | 5 +-- pylint/checkers/spelling.py | 9 +++--- pylint/checkers/stdlib.py | 5 ++- pylint/checkers/strings.py | 8 ++--- pylint/checkers/typecheck.py | 29 ++++++++--------- pylint/checkers/utils.py | 10 +++--- pylint/checkers/variables.py | 16 ++++------ pylint/config.py | 22 +++++++------ pylint/epylint.py | 4 +-- pylint/extensions/bad_builtin.py | 2 +- pylint/extensions/check_elif.py | 3 +- pylint/extensions/comparetozero.py | 3 +- pylint/extensions/docparams.py | 6 ++-- pylint/extensions/docstyle.py | 2 +- pylint/extensions/emptystring.py | 3 +- pylint/extensions/mccabe.py | 7 ++--- pylint/extensions/overlapping_exceptions.py | 4 +-- pylint/extensions/redefined_variable_type.py | 2 +- pylint/graph.py | 4 +-- pylint/lint.py | 26 +++++++--------- pylint/message/__init__.py | 4 +-- pylint/message/message_handler_mix_in.py | 4 +-- pylint/pyreverse/diadefslib.py | 2 +- pylint/pyreverse/diagrams.py | 3 +- pylint/pyreverse/inspector.py | 7 +---- pylint/pyreverse/main.py | 4 +-- pylint/pyreverse/writer.py | 2 +- pylint/reporters/__init__.py | 3 +- pylint/reporters/text.py | 5 ++- pylint/reporters/ureports/__init__.py | 1 - pylint/reporters/ureports/text_writer.py | 1 - pylint/test/conftest.py | 2 +- pylint/test/extensions/data/docstring.py | 1 + pylint/test/extensions/test_bad_builtin.py | 1 - pylint/test/extensions/test_check_docs.py | 7 ++--- .../test/extensions/test_check_docs_utils.py | 5 ++- .../test/extensions/test_check_raise_docs.py | 4 +-- .../test/extensions/test_check_return_docs.py | 4 +-- .../test/extensions/test_check_yields_docs.py | 4 +-- pylint/test/extensions/test_docstyle.py | 1 - pylint/test/extensions/test_emptystring.py | 1 + .../extensions/test_overlapping_exceptions.py | 6 ++-- pylint/test/extensions/test_redefined.py | 1 - pylint/test/test_func.py | 4 +-- pylint/test/test_functional.py | 11 +++---- pylint/test/test_import_graph.py | 5 ++- pylint/test/test_regr.py | 5 ++- pylint/test/test_self.py | 15 ++++----- pylint/test/unittest_checker_base.py | 1 + pylint/test/unittest_checker_classes.py | 1 + pylint/test/unittest_checker_format.py | 9 ++---- pylint/test/unittest_checker_imports.py | 3 +- pylint/test/unittest_checker_misc.py | 2 +- pylint/test/unittest_checker_python3.py | 6 ++-- pylint/test/unittest_checker_similar.py | 4 +-- pylint/test/unittest_checker_spelling.py | 5 ++- pylint/test/unittest_checker_stdlib.py | 2 +- pylint/test/unittest_checker_strings.py | 1 - pylint/test/unittest_checker_typecheck.py | 3 +- pylint/test/unittest_checker_variables.py | 4 +-- pylint/test/unittest_checkers_utils.py | 2 +- pylint/test/unittest_config.py | 2 +- pylint/test/unittest_pyreverse_diadefs.py | 6 ++-- pylint/test/unittest_pyreverse_inspector.py | 6 ++-- pylint/test/unittest_pyreverse_writer.py | 7 ++--- pylint/test/unittest_reporters_json.py | 3 +- pylint/test/unittest_reporting.py | 8 ++--- pylint/test/utils/unittest_utils.py | 2 +- pylint/testutils.py | 12 +++---- pylint/utils/__init__.py | 2 +- pylint/utils/file_state.py | 1 + pylint/utils/utils.py | 3 +- setup.py | 10 +++--- 96 files changed, 245 insertions(+), 289 deletions(-) diff --git a/bin/epylint b/bin/epylint index 4aad65703b..a55adf4c3e 100755 --- a/bin/epylint +++ b/bin/epylint @@ -1,3 +1,4 @@ #!/usr/bin/env python from pylint import epylint + epylint.Run() diff --git a/bin/pylint b/bin/pylint index e3e520f0c7..162e96d407 100755 --- a/bin/pylint +++ b/bin/pylint @@ -1,3 +1,4 @@ #!/usr/bin/env python from pylint import run_pylint + run_pylint() diff --git a/bin/pyreverse b/bin/pyreverse index 71269e769f..d0b080dfc2 100755 --- a/bin/pyreverse +++ b/bin/pyreverse @@ -1,3 +1,4 @@ #!/usr/bin/env python from pylint import run_pyreverse + run_pyreverse() diff --git a/bin/symilar b/bin/symilar index ef94462473..7125d1f91c 100755 --- a/bin/symilar +++ b/bin/symilar @@ -1,3 +1,4 @@ #!/usr/bin/env python from pylint import run_symilar + run_symilar() diff --git a/doc/conf.py b/doc/conf.py index ada9ec4933..16bfb7eb9f 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -11,10 +11,17 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys import os +import sys from datetime import datetime +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +from pylint.__pkginfo__ import version + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -51,12 +58,6 @@ current_year = datetime.utcnow().year copyright = '2003-{year}, Logilab, PyCQA and contributors'.format(year=current_year) -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -from pylint.__pkginfo__ import version # The full version, including alpha/beta/rc tags. release = version diff --git a/doc/exts/pylint_extensions.py b/doc/exts/pylint_extensions.py index e6b1eb46f1..2e1d821635 100755 --- a/doc/exts/pylint_extensions.py +++ b/doc/exts/pylint_extensions.py @@ -13,7 +13,6 @@ from pylint.lint import PyLinter - # Some modules have been renamed and deprecated under their old names. # Skip documenting these modules since: # 1) They are deprecated, why document them moving forward? diff --git a/doc/exts/pylint_features.py b/doc/exts/pylint_features.py index fcb2d10e1e..2ff0e52a76 100755 --- a/doc/exts/pylint_features.py +++ b/doc/exts/pylint_features.py @@ -11,6 +11,7 @@ from pylint.lint import PyLinter + def builder_inited(app): # PACKAGE/docs/exts/pylint_extensions.py --> PACKAGE/ base_path = os.path.dirname( diff --git a/examples/custom.py b/examples/custom.py index 54c2937b10..039b018b8b 100644 --- a/examples/custom.py +++ b/examples/custom.py @@ -1,7 +1,7 @@ import astroid -from pylint.interfaces import IAstroidChecker from pylint.checkers import BaseChecker +from pylint.interfaces import IAstroidChecker # This is our checker class. diff --git a/examples/custom_raw.py b/examples/custom_raw.py index a866667244..fb28d49311 100644 --- a/examples/custom_raw.py +++ b/examples/custom_raw.py @@ -1,5 +1,6 @@ -from pylint.interfaces import IRawChecker from pylint.checkers import BaseChecker +from pylint.interfaces import IRawChecker + class MyRawChecker(BaseChecker): """check for line continuations with '\' instead of using triple @@ -31,4 +32,3 @@ def process_module(self, node): def register(linter): """required method to auto register this checker""" linter.register_checker(MyRawChecker(linter)) - diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py index 1a4c0009f1..f745037bc1 100644 --- a/pylint/checkers/__init__.py +++ b/pylint/checkers/__init__.py @@ -45,9 +45,9 @@ from typing import Any from pylint.config import OptionsProviderMixIn +from pylint.interfaces import UNDEFINED from pylint.reporters import diff_string from pylint.utils import register_plugins -from pylint.interfaces import UNDEFINED def table_lines_from_stats(stats, old_stats, columns): diff --git a/pylint/checkers/async.py b/pylint/checkers/async.py index 21feba0355..c33071e84b 100644 --- a/pylint/checkers/async.py +++ b/pylint/checkers/async.py @@ -9,13 +9,10 @@ import sys import astroid -from astroid import bases -from astroid import exceptions +from astroid import bases, exceptions -from pylint import checkers +from pylint import checkers, interfaces, utils from pylint.checkers import utils as checker_utils -from pylint import interfaces -from pylint import utils from pylint.checkers.utils import decorated_with diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index e2e35f3dd4..bce4ae7fd3 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -40,22 +40,19 @@ import builtins import collections import itertools -import sys import re +import sys from typing import Pattern import astroid import astroid.bases import astroid.scoped_nodes -from pylint import checkers -from pylint import exceptions -from pylint import interfaces +import pylint.utils as lint_utils +from pylint import checkers, exceptions, interfaces, reporters from pylint.checkers import utils -from pylint import reporters from pylint.checkers.utils import get_node_last_lineno from pylint.reporters.ureports import nodes as reporter_nodes -import pylint.utils as lint_utils class NamingStyle: diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 543b27ecf7..d75107136c 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -32,38 +32,37 @@ from __future__ import generators import collections -from itertools import chain, zip_longest import sys +from itertools import chain, zip_longest import astroid -from astroid.bases import Generator, BUILTINS -from astroid.exceptions import InconsistentMroError, DuplicateBasesError -from astroid import decorators -from astroid import objects +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.interfaces import IAstroidChecker + from pylint.checkers import BaseChecker from pylint.checkers.utils import ( PYMETHODS, SPECIAL_METHODS_PARAMS, - overrides_a_method, check_messages, + class_is_abstract, + decorated_with, + decorated_with_property, + has_known_bases, is_attr_private, is_attr_protected, - node_frame_class, is_builtin_object, - decorated_with_property, - unimplemented_abstract_methods, - decorated_with, - class_is_abstract, - safe_infer, - has_known_bases, - is_iterable, is_comprehension, + is_iterable, + node_frame_class, + overrides_a_method, + safe_infer, + unimplemented_abstract_methods, ) +from pylint.interfaces import IAstroidChecker from pylint.utils import get_global_option - if sys.version_info >= (3, 0): NEXT_METHOD = "__next__" else: diff --git a/pylint/checkers/design_analysis.py b/pylint/checkers/design_analysis.py index e9f0a04707..b6fd9913ce 100644 --- a/pylint/checkers/design_analysis.py +++ b/pylint/checkers/design_analysis.py @@ -17,18 +17,16 @@ """check for signs of poor design""" -from collections import defaultdict import re +from collections import defaultdict import astroid -from astroid import If, BoolOp -from astroid import decorators +from astroid import BoolOp, If, decorators -from pylint.interfaces import IAstroidChecker +from pylint import utils from pylint.checkers import BaseChecker from pylint.checkers.utils import check_messages -from pylint import utils - +from pylint.interfaces import IAstroidChecker MSGS = { "R0901": ( diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index c43af53af3..6521a5bd50 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -29,9 +29,8 @@ import astroid -from pylint import checkers +from pylint import checkers, interfaces from pylint.checkers import utils -from pylint import interfaces def _builtin_exceptions(): diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index d1aa8e91bc..84b6b273ea 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -50,10 +50,10 @@ from astroid import nodes -from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker from pylint.checkers import BaseTokenChecker from pylint.checkers.utils import check_messages -from pylint.utils import WarningScope, OPTION_RGX +from pylint.interfaces import IAstroidChecker, IRawChecker, ITokenChecker +from pylint.utils import OPTION_RGX, WarningScope _ASYNC_TOKEN = "async" _CONTINUATION_BLOCK_OPENERS = [ diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index 07ae01fec7..82ef94a079 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -30,27 +30,27 @@ """imports checkers for Python code""" import collections -from distutils import sysconfig +import copy import os import sys -import copy +from distutils import sysconfig import astroid +import isort from astroid import are_exclusive, decorators from astroid.modutils import get_module_part, is_standard_module -import isort -from pylint.interfaces import IAstroidChecker -from pylint.utils import get_global_option -from pylint.exceptions import EmptyReportError from pylint.checkers import BaseChecker from pylint.checkers.utils import ( check_messages, - node_ignores_exception, is_from_fallback_block, + node_ignores_exception, ) -from pylint.graph import get_cycles, DotBackend -from pylint.reporters.ureports.nodes import VerbatimText, Paragraph +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): diff --git a/pylint/checkers/logging.py b/pylint/checkers/logging.py index 13646cdcef..7d8dd5b69b 100644 --- a/pylint/checkers/logging.py +++ b/pylint/checkers/logging.py @@ -21,12 +21,10 @@ import astroid -from pylint import checkers -from pylint import interfaces +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", diff --git a/pylint/checkers/misc.py b/pylint/checkers/misc.py index 26dc0a3df0..d12c2639e5 100644 --- a/pylint/checkers/misc.py +++ b/pylint/checkers/misc.py @@ -20,14 +20,13 @@ # pylint: disable=W0511 -import tokenize - import re +import tokenize -from pylint.interfaces import IRawChecker, ITokenChecker from pylint.checkers import BaseChecker -from pylint.utils import OPTION_RGX +from pylint.interfaces import IRawChecker, ITokenChecker from pylint.message import MessagesHandlerMixIn +from pylint.utils import OPTION_RGX class ByIdManagedMessagesChecker(BaseChecker): diff --git a/pylint/checkers/newstyle.py b/pylint/checkers/newstyle.py index 89d31653be..97e2fcb655 100644 --- a/pylint/checkers/newstyle.py +++ b/pylint/checkers/newstyle.py @@ -17,9 +17,9 @@ import astroid -from pylint.interfaces import IAstroidChecker from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages, node_frame_class, has_known_bases +from pylint.checkers.utils import check_messages, has_known_bases, node_frame_class +from pylint.interfaces import IAstroidChecker MSGS = { "E1003": ( diff --git a/pylint/checkers/python3.py b/pylint/checkers/python3.py index d577c918c6..43025d0c8b 100644 --- a/pylint/checkers/python3.py +++ b/pylint/checkers/python3.py @@ -26,21 +26,20 @@ """Check Python 2 code for Python 2/3 source-compatible issues.""" from __future__ import absolute_import, print_function -from collections import namedtuple import re import sys import tokenize +from collections import namedtuple from typing import FrozenSet import astroid from astroid import bases from pylint import checkers, interfaces -from pylint.checkers.utils import node_ignores_exception, find_try_except_wrapper_node -from pylint.interfaces import INFERENCE_FAILURE, INFERENCE -from pylint.utils import WarningScope from pylint.checkers import utils - +from pylint.checkers.utils import find_try_except_wrapper_node, node_ignores_exception +from pylint.interfaces import INFERENCE, INFERENCE_FAILURE +from pylint.utils import WarningScope _ZERO = re.compile("^0+$") diff --git a/pylint/checkers/raw_metrics.py b/pylint/checkers/raw_metrics.py index 7ab165675c..49ee2fa2f3 100644 --- a/pylint/checkers/raw_metrics.py +++ b/pylint/checkers/raw_metrics.py @@ -18,9 +18,9 @@ import tokenize from typing import Any -from pylint.interfaces import ITokenChecker -from pylint.exceptions import EmptyReportError from pylint.checkers import BaseTokenChecker +from pylint.exceptions import EmptyReportError +from pylint.interfaces import ITokenChecker from pylint.reporters import diff_string from pylint.reporters.ureports.nodes import Table diff --git a/pylint/checkers/refactoring.py b/pylint/checkers/refactoring.py index 6a349c0677..cab521aaac 100644 --- a/pylint/checkers/refactoring.py +++ b/pylint/checkers/refactoring.py @@ -22,21 +22,18 @@ """Looks for code which can be refactored.""" import builtins -from functools import reduce - import collections import itertools import tokenize +from functools import reduce import astroid from astroid import decorators -from pylint import interfaces -from pylint import checkers +from pylint import checkers, interfaces from pylint import utils as lint_utils from pylint.checkers import utils - KNOWN_INFINITE_ITERATORS = {"itertools.count"} diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py index 2e45ce9c68..d79785d667 100644 --- a/pylint/checkers/similar.py +++ b/pylint/checkers/similar.py @@ -18,16 +18,17 @@ """ from __future__ import print_function + import sys from collections import defaultdict from itertools import groupby import astroid -from pylint.utils import decoding_stream -from pylint.interfaces import IRawChecker 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: diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py index df99f6b3e1..f1952e89fc 100644 --- a/pylint/checkers/spelling.py +++ b/pylint/checkers/spelling.py @@ -19,8 +19,12 @@ """ import os -import tokenize 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 @@ -43,9 +47,6 @@ class Chunker: # type: ignore pass -from pylint.interfaces import ITokenChecker, IAstroidChecker -from pylint.checkers import BaseTokenChecker -from pylint.checkers.utils import check_messages if enchant is not None: br = enchant.Broker() diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index 7dad31d287..778ea98df2 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -26,10 +26,9 @@ import astroid from astroid.bases import Instance from astroid.node_classes import Const -from pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers import utils +from pylint.checkers import BaseChecker, utils +from pylint.interfaces import IAstroidChecker OPEN_FILES = {"open", "file"} UNITTEST_CASE = "unittest.case" diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py index d7caf85a14..9b22e5244c 100644 --- a/pylint/checkers/strings.py +++ b/pylint/checkers/strings.py @@ -23,18 +23,18 @@ """ import builtins +import numbers import sys import tokenize -import numbers from collections import Counter import astroid from astroid.arguments import CallSite from astroid.node_classes import Const -from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker -from pylint.checkers import BaseChecker, BaseTokenChecker -from pylint.checkers import utils + +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") diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 417662b877..fa95d7cc4a 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -47,35 +47,32 @@ from functools import singledispatch import astroid -import astroid.context import astroid.arguments +import astroid.context import astroid.nodes -from astroid import exceptions, decorators +from astroid import bases, decorators, exceptions, modutils, objects from astroid.interpreter import dunder_lookup -from astroid import objects -from astroid import bases -from astroid import modutils -from pylint.interfaces import IAstroidChecker, INFERENCE from pylint.checkers import BaseChecker from pylint.checkers.utils import ( - is_super, check_messages, - decorated_with_property, decorated_with, - node_ignores_exception, - is_iterable, - is_mapping, - supports_membership_test, + decorated_with_property, + has_known_bases, + is_builtin_object, is_comprehension, is_inside_abstract_class, + is_iterable, + is_mapping, + is_super, + node_ignores_exception, + safe_infer, + supports_delitem, supports_getitem, + supports_membership_test, supports_setitem, - supports_delitem, - safe_infer, - has_known_bases, - is_builtin_object, ) +from pylint.interfaces import INFERENCE, IAstroidChecker from pylint.utils import get_global_option BUILTINS = builtins.__name__ diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index dbbba49b0e..38dca2a386 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -30,20 +30,20 @@ """some functions that may be useful for various checkers """ import builtins -from functools import lru_cache, partial import itertools import numbers import re -import sys import string -from typing import Optional, Iterable, Tuple, Callable, Set, Union, Match, Dict, List -import _string # pylint: disable=wrong-import-position, wrong-import-order +import sys +from functools import lru_cache, partial +from typing import Callable, Dict, Iterable, List, Match, Optional, Set, Tuple, Union import astroid -from astroid.exceptions import _NonDeducibleTypeHierarchy from astroid import bases as _bases from astroid import scoped_nodes +from astroid.exceptions import _NonDeducibleTypeHierarchy +import _string # pylint: disable=wrong-import-position, wrong-import-order BUILTINS_NAME = builtins.__name__ COMP_NODE_TYPES = ( diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 85de92aaa1..02974a19a1 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -34,26 +34,22 @@ """variables checkers for Python code """ +import collections import copy -from functools import lru_cache import itertools -import collections import os -import sys import re +import sys +from functools import lru_cache import astroid -from astroid import decorators -from astroid import modutils -from astroid import objects +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 IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH +from pylint.interfaces import HIGH, INFERENCE, INFERENCE_FAILURE, IAstroidChecker from pylint.utils import get_global_option -from pylint.checkers import BaseChecker -from pylint.checkers import utils - SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$") FUTURE = "__future__" diff --git a/pylint/config.py b/pylint/config.py index 0477c7914f..a913f86199 100644 --- a/pylint/config.py +++ b/pylint/config.py @@ -35,15 +35,9 @@ """ from __future__ import print_function -# TODO(cpopa): this module contains the logic for the -# configuration parser and for the command line parser, -# but it's really coupled to optparse's internals. -# The code was copied almost verbatim from logilab.common, -# in order to not depend on it anymore and it will definitely -# need a cleanup. It could be completely reengineered as well. - -import contextlib import collections +import configparser +import contextlib import copy import io import optparse @@ -53,10 +47,18 @@ import sys import time -import configparser - from pylint import utils +# TODO(cpopa): this module contains the logic for the +# configuration parser and for the command line parser, +# but it's really coupled to optparse's internals. +# The code was copied almost verbatim from logilab.common, +# in order to not depend on it anymore and it will definitely +# need a cleanup. It could be completely reengineered as well. + + + + USER_HOME = os.path.expanduser("~") if "PYLINTHOME" in os.environ: diff --git a/pylint/epylint.py b/pylint/epylint.py index 50c3a99f9c..c2d4a21d8c 100755 --- a/pylint/epylint.py +++ b/pylint/epylint.py @@ -56,10 +56,10 @@ import os import os.path as osp -import sys import shlex -from subprocess import Popen, PIPE +import sys from io import StringIO +from subprocess import PIPE, Popen def _get_env(): diff --git a/pylint/extensions/bad_builtin.py b/pylint/extensions/bad_builtin.py index 2609d3168f..f5533b8362 100644 --- a/pylint/extensions/bad_builtin.py +++ b/pylint/extensions/bad_builtin.py @@ -7,11 +7,11 @@ import sys import astroid + from pylint.checkers import BaseChecker from pylint.checkers.utils import check_messages from pylint.interfaces import IAstroidChecker - BAD_FUNCTIONS = ["map", "filter"] if sys.version_info < (3, 0): BAD_FUNCTIONS.append("input") diff --git a/pylint/extensions/check_elif.py b/pylint/extensions/check_elif.py index 9ef0b3fa33..67555b16ad 100644 --- a/pylint/extensions/check_elif.py +++ b/pylint/extensions/check_elif.py @@ -8,9 +8,10 @@ # For details: https://github.com/PyCQA/pylint/blob/master/COPYING import astroid + from pylint.checkers import BaseTokenChecker from pylint.checkers.utils import check_messages -from pylint.interfaces import ITokenChecker, IAstroidChecker +from pylint.interfaces import IAstroidChecker, ITokenChecker class ElseifUsedChecker(BaseTokenChecker): diff --git a/pylint/extensions/comparetozero.py b/pylint/extensions/comparetozero.py index 92063ab180..e31f488751 100644 --- a/pylint/extensions/comparetozero.py +++ b/pylint/extensions/comparetozero.py @@ -11,8 +11,7 @@ import astroid -from pylint import interfaces -from pylint import checkers +from pylint import checkers, interfaces from pylint.checkers import utils diff --git a/pylint/extensions/docparams.py b/pylint/extensions/docparams.py index 771d64c04a..939e2a3fa5 100644 --- a/pylint/extensions/docparams.py +++ b/pylint/extensions/docparams.py @@ -16,14 +16,14 @@ """Pylint plugin for checking in Sphinx, Google, or Numpy style docstrings """ -from __future__ import print_function, division, absolute_import +from __future__ import absolute_import, division, print_function import astroid -from pylint.interfaces import IAstroidChecker +import pylint.extensions._check_docs_utils as utils from pylint.checkers import BaseChecker from pylint.checkers import utils as checker_utils -import pylint.extensions._check_docs_utils as utils +from pylint.interfaces import IAstroidChecker class DocstringParameterChecker(BaseChecker): diff --git a/pylint/extensions/docstyle.py b/pylint/extensions/docstyle.py index 1f50a414ee..36f506fcf4 100644 --- a/pylint/extensions/docstyle.py +++ b/pylint/extensions/docstyle.py @@ -9,8 +9,8 @@ import linecache from pylint import checkers -from pylint.interfaces import IAstroidChecker, HIGH from pylint.checkers.utils import check_messages +from pylint.interfaces import HIGH, IAstroidChecker class DocStringStyleChecker(checkers.BaseChecker): diff --git a/pylint/extensions/emptystring.py b/pylint/extensions/emptystring.py index ddca8f7d5e..04021d5046 100644 --- a/pylint/extensions/emptystring.py +++ b/pylint/extensions/emptystring.py @@ -11,8 +11,7 @@ import astroid -from pylint import interfaces -from pylint import checkers +from pylint import checkers, interfaces from pylint.checkers import utils diff --git a/pylint/extensions/mccabe.py b/pylint/extensions/mccabe.py index 41eca280fc..69bc1775ba 100644 --- a/pylint/extensions/mccabe.py +++ b/pylint/extensions/mccabe.py @@ -9,10 +9,9 @@ from __future__ import absolute_import -from mccabe import ( - PathGraph as Mccabe_PathGraph, - PathGraphingAstVisitor as Mccabe_PathGraphingAstVisitor, -) +from mccabe import PathGraph as Mccabe_PathGraph +from mccabe import PathGraphingAstVisitor as Mccabe_PathGraphingAstVisitor + from pylint import checkers from pylint.checkers.utils import check_messages from pylint.interfaces import HIGH, IAstroidChecker diff --git a/pylint/extensions/overlapping_exceptions.py b/pylint/extensions/overlapping_exceptions.py index 3b1454cae0..be2208c9b1 100644 --- a/pylint/extensions/overlapping_exceptions.py +++ b/pylint/extensions/overlapping_exceptions.py @@ -7,10 +7,8 @@ import astroid -from pylint import interfaces -from pylint import checkers +from pylint import checkers, interfaces from pylint.checkers import utils - from pylint.checkers.exceptions import _annotated_unpack_infer diff --git a/pylint/extensions/redefined_variable_type.py b/pylint/extensions/redefined_variable_type.py index a58f04e2d1..cfe4754c94 100644 --- a/pylint/extensions/redefined_variable_type.py +++ b/pylint/extensions/redefined_variable_type.py @@ -8,11 +8,11 @@ # For details: https://github.com/PyCQA/pylint/blob/master/COPYING import astroid + from pylint.checkers import BaseChecker from pylint.checkers.utils import check_messages, is_none, node_type from pylint.interfaces import IAstroidChecker - BUILTINS = "builtins" diff --git a/pylint/graph.py b/pylint/graph.py index f7790dae8f..3eb4802275 100644 --- a/pylint/graph.py +++ b/pylint/graph.py @@ -11,11 +11,11 @@ (dot generation adapted from pypy/translator/tool/make_dot.py) """ -import os.path as osp +import codecs import os +import os.path as osp import sys import tempfile -import codecs def target_info_from_filename(filename): diff --git a/pylint/lint.py b/pylint/lint.py index c012c0b296..d02410fa08 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -62,31 +62,29 @@ import collections import contextlib -from io import TextIOWrapper import operator import os - -try: - import multiprocessing -except ImportError: - multiprocessing = None # type: ignore import sys import tokenize import warnings +from io import TextIOWrapper import astroid +from astroid import modutils from astroid.__pkginfo__ import version as astroid_version from astroid.builder import AstroidBuilder -from astroid import modutils -from pylint import checkers -from pylint import interfaces -from pylint import reporters -from pylint.message import MessagesStore, Message, MSG_TYPES, MessagesHandlerMixIn -from pylint.utils import FileState, ASTWalker, ReportsHandlerMixIn, OPTION_RGX, utils -from pylint import exceptions -from pylint import config + +from pylint import checkers, config, exceptions, interfaces, reporters from pylint.__pkginfo__ import version +from pylint.message import MSG_TYPES, Message, MessagesHandlerMixIn, MessagesStore from pylint.reporters.ureports import nodes as report_nodes +from pylint.utils import OPTION_RGX, ASTWalker, FileState, ReportsHandlerMixIn, utils + +try: + import multiprocessing +except ImportError: + multiprocessing = None # type: ignore + MANAGER = astroid.MANAGER diff --git a/pylint/message/__init__.py b/pylint/message/__init__.py index 694e2eb136..4b71f57b9d 100644 --- a/pylint/message/__init__.py +++ b/pylint/message/__init__.py @@ -39,16 +39,16 @@ """All the classes related to Message handling.""" +from pylint.message.build_message_definition import build_message_def from pylint.message.constants import ( - MSG_STATE_CONFIDENCE, _SCOPE_EXEMPT, + MSG_STATE_CONFIDENCE, MSG_STATE_SCOPE_CONFIG, MSG_STATE_SCOPE_MODULE, MSG_TYPES, MSG_TYPES_LONG, MSG_TYPES_STATUS, ) -from pylint.message.build_message_definition import build_message_def from pylint.message.message import Message from pylint.message.message_definition import MessageDefinition from pylint.message.message_handler_mix_in import MessagesHandlerMixIn diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index d01be8ff3a..7336979c25 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -10,6 +10,7 @@ from pylint.exceptions import InvalidMessageError, UnknownMessageError from pylint.interfaces import UNDEFINED +from pylint.message.build_message_definition import build_message_def from pylint.message.constants import ( _MSG_ORDER, _SCOPE_EXEMPT, @@ -20,14 +21,13 @@ MSG_TYPES_STATUS, ) from pylint.message.message import Message -from pylint.utils.warning_scope import WarningScope -from pylint.message.build_message_definition import build_message_def from pylint.utils.utils import ( _format_option_value, category_id, get_module_and_frameid, normalize_text, ) +from pylint.utils.warning_scope import WarningScope def _rest_format_section(stream, section, options, doc=None): diff --git a/pylint/pyreverse/diadefslib.py b/pylint/pyreverse/diadefslib.py index e64614fc7e..de4e9fd4aa 100644 --- a/pylint/pyreverse/diadefslib.py +++ b/pylint/pyreverse/diadefslib.py @@ -19,7 +19,7 @@ import astroid -from pylint.pyreverse.diagrams import PackageDiagram, ClassDiagram +from pylint.pyreverse.diagrams import ClassDiagram, PackageDiagram from pylint.pyreverse.utils import LocalsVisitor BUILTINS_NAME = "builtins" diff --git a/pylint/pyreverse/diagrams.py b/pylint/pyreverse/diagrams.py index afd7ffee4f..b53b845c2b 100644 --- a/pylint/pyreverse/diagrams.py +++ b/pylint/pyreverse/diagrams.py @@ -12,8 +12,9 @@ """ import astroid -from pylint.pyreverse.utils import is_interface, FilterMixIn + from pylint.checkers.utils import decorated_with_property +from pylint.pyreverse.utils import FilterMixIn, is_interface class Figure: diff --git a/pylint/pyreverse/inspector.py b/pylint/pyreverse/inspector.py index eca4b67aa7..b4df388be4 100644 --- a/pylint/pyreverse/inspector.py +++ b/pylint/pyreverse/inspector.py @@ -18,12 +18,7 @@ import traceback import astroid -from astroid import bases -from astroid import exceptions -from astroid import manager -from astroid import modutils -from astroid import node_classes - +from astroid import bases, exceptions, manager, modutils, node_classes from pylint.pyreverse import utils diff --git a/pylint/pyreverse/main.py b/pylint/pyreverse/main.py index 2be7c486da..8d30eacbad 100644 --- a/pylint/pyreverse/main.py +++ b/pylint/pyreverse/main.py @@ -20,9 +20,9 @@ import sys from pylint.config import ConfigurationMixIn -from pylint.pyreverse.inspector import Linker, project_from_files -from pylint.pyreverse.diadefslib import DiadefsHandler from pylint.pyreverse import writer +from pylint.pyreverse.diadefslib import DiadefsHandler +from pylint.pyreverse.inspector import Linker, project_from_files from pylint.pyreverse.utils import insert_default_options OPTIONS = ( diff --git a/pylint/pyreverse/writer.py b/pylint/pyreverse/writer.py index c97ba9bf5e..609b1efcd3 100644 --- a/pylint/pyreverse/writer.py +++ b/pylint/pyreverse/writer.py @@ -13,9 +13,9 @@ """Utilities for creating VCG and Dot diagrams""" +from pylint.graph import DotBackend from pylint.pyreverse.utils import is_exception from pylint.pyreverse.vcgutils import VCGPrinter -from pylint.graph import DotBackend class DiagramWriter: diff --git a/pylint/reporters/__init__.py b/pylint/reporters/__init__.py index c09ebdd0c2..98b0364a22 100644 --- a/pylint/reporters/__init__.py +++ b/pylint/reporters/__init__.py @@ -19,12 +19,11 @@ """utilities methods and classes for reporters""" from __future__ import print_function -import sys import locale import os +import sys import warnings - CMPS = ["=", "-", "+"] # py3k has no more cmp builtin diff --git a/pylint/reporters/text.py b/pylint/reporters/text.py index 4c682ae05e..6af8706d0c 100644 --- a/pylint/reporters/text.py +++ b/pylint/reporters/text.py @@ -21,15 +21,14 @@ from __future__ import print_function import os -import warnings import sys +import warnings +from pylint import utils from pylint.interfaces import IReporter from pylint.reporters import BaseReporter -from pylint import utils from pylint.reporters.ureports.text_writer import TextWriter - TITLE_UNDERLINES = ["", "=", "-", "."] ANSI_PREFIX = "\033[" diff --git a/pylint/reporters/ureports/__init__.py b/pylint/reporters/ureports/__init__.py index 56a829facd..361552be78 100644 --- a/pylint/reporters/ureports/__init__.py +++ b/pylint/reporters/ureports/__init__.py @@ -13,7 +13,6 @@ """ import os import sys - from io import StringIO diff --git a/pylint/reporters/ureports/text_writer.py b/pylint/reporters/ureports/text_writer.py index 9d9e1ad342..acb6a9ac84 100644 --- a/pylint/reporters/ureports/text_writer.py +++ b/pylint/reporters/ureports/text_writer.py @@ -10,7 +10,6 @@ from pylint.reporters.ureports import BaseWriter - TITLE_UNDERLINES = ["", "=", "-", "`", ".", "~", "^"] BULLETS = ["*", "-"] diff --git a/pylint/test/conftest.py b/pylint/test/conftest.py index d75522bedc..985866893d 100644 --- a/pylint/test/conftest.py +++ b/pylint/test/conftest.py @@ -1,10 +1,10 @@ # pylint: disable=redefined-outer-name import os + import pytest from pylint import checkers from pylint.lint import PyLinter - # pylint: disable=no-name-in-module from pylint.testutils import MinimalTestReporter diff --git a/pylint/test/extensions/data/docstring.py b/pylint/test/extensions/data/docstring.py index 5410a9bb40..5a2c3fe63b 100644 --- a/pylint/test/extensions/data/docstring.py +++ b/pylint/test/extensions/data/docstring.py @@ -38,3 +38,4 @@ def method6(self): def method7(self): u"""Test OK 3 with unicode string""" + diff --git a/pylint/test/extensions/test_bad_builtin.py b/pylint/test/extensions/test_bad_builtin.py index 7d5d7f3045..2fd8a95e61 100644 --- a/pylint/test/extensions/test_bad_builtin.py +++ b/pylint/test/extensions/test_bad_builtin.py @@ -14,7 +14,6 @@ from pylint.extensions.bad_builtin import BadBuiltinChecker from pylint.lint import fix_import_path - EXPECTED = [ "Used builtin function 'map'. Using a list comprehension can be clearer.", "Used builtin function 'filter'. Using a list comprehension can be clearer.", diff --git a/pylint/test/extensions/test_check_docs.py b/pylint/test/extensions/test_check_docs.py index bad6baf077..63825c415f 100644 --- a/pylint/test/extensions/test_check_docs.py +++ b/pylint/test/extensions/test_check_docs.py @@ -16,16 +16,15 @@ """Unit tests for the pylint checkers in :mod:`pylint.extensions.check_docs`, in particular the parameter documentation checker `DocstringChecker` """ -from __future__ import division, print_function, absolute_import +from __future__ import absolute_import, division, print_function import sys -import pytest - import astroid -from pylint.testutils import CheckerTestCase, Message, set_config +import pytest from pylint.extensions.docparams import DocstringParameterChecker +from pylint.testutils import CheckerTestCase, Message, set_config class TestParamDocChecker(CheckerTestCase): diff --git a/pylint/test/extensions/test_check_docs_utils.py b/pylint/test/extensions/test_check_docs_utils.py index 28c9a05912..26af9db9d0 100644 --- a/pylint/test/extensions/test_check_docs_utils.py +++ b/pylint/test/extensions/test_check_docs_utils.py @@ -9,11 +9,10 @@ """Unit tests for the pylint checkers in :mod:`pylint.extensions.check_docs`, in particular the parameter documentation checker `DocstringChecker` """ -from __future__ import division, print_function, absolute_import - -import pytest +from __future__ import absolute_import, division, print_function import astroid +import pytest import pylint.extensions._check_docs_utils as utils diff --git a/pylint/test/extensions/test_check_raise_docs.py b/pylint/test/extensions/test_check_raise_docs.py index 64ffe5b265..c51a1fdaae 100644 --- a/pylint/test/extensions/test_check_raise_docs.py +++ b/pylint/test/extensions/test_check_raise_docs.py @@ -10,12 +10,12 @@ """Unit tests for the raised exception documentation checking in the `DocstringChecker` in :mod:`pylint.extensions.check_docs` """ -from __future__ import division, print_function, absolute_import +from __future__ import absolute_import, division, print_function import astroid -from pylint.testutils import CheckerTestCase, Message, set_config from pylint.extensions.docparams import DocstringParameterChecker +from pylint.testutils import CheckerTestCase, Message, set_config class TestDocstringCheckerRaise(CheckerTestCase): diff --git a/pylint/test/extensions/test_check_return_docs.py b/pylint/test/extensions/test_check_return_docs.py index db44886ef3..29de723101 100644 --- a/pylint/test/extensions/test_check_return_docs.py +++ b/pylint/test/extensions/test_check_return_docs.py @@ -12,12 +12,12 @@ """Unit tests for the return documentation checking in the `DocstringChecker` in :mod:`pylint.extensions.check_docs` """ -from __future__ import division, print_function, absolute_import +from __future__ import absolute_import, division, print_function import astroid -from pylint.testutils import CheckerTestCase, Message, set_config from pylint.extensions.docparams import DocstringParameterChecker +from pylint.testutils import CheckerTestCase, Message, set_config class TestDocstringCheckerReturn(CheckerTestCase): diff --git a/pylint/test/extensions/test_check_yields_docs.py b/pylint/test/extensions/test_check_yields_docs.py index 0d800b7e79..85c75c02d6 100644 --- a/pylint/test/extensions/test_check_yields_docs.py +++ b/pylint/test/extensions/test_check_yields_docs.py @@ -9,12 +9,12 @@ """Unit tests for the yield documentation checking in the `DocstringChecker` in :mod:`pylint.extensions.check_docs` """ -from __future__ import division, print_function, absolute_import +from __future__ import absolute_import, division, print_function import astroid -from pylint.testutils import CheckerTestCase, Message, set_config from pylint.extensions.docparams import DocstringParameterChecker +from pylint.testutils import CheckerTestCase, Message, set_config class TestDocstringCheckerYield(CheckerTestCase): diff --git a/pylint/test/extensions/test_docstyle.py b/pylint/test/extensions/test_docstyle.py index 7ced9a15e2..c4d3d881c2 100644 --- a/pylint/test/extensions/test_docstyle.py +++ b/pylint/test/extensions/test_docstyle.py @@ -14,7 +14,6 @@ from pylint.extensions.docstyle import DocStringStyleChecker - EXPECTED_MSGS = [ 'First line empty in function docstring', 'First line empty in class docstring', diff --git a/pylint/test/extensions/test_emptystring.py b/pylint/test/extensions/test_emptystring.py index c935b3bb87..c7ff30b50c 100644 --- a/pylint/test/extensions/test_emptystring.py +++ b/pylint/test/extensions/test_emptystring.py @@ -11,6 +11,7 @@ """ import os.path as osp + import pytest from pylint.extensions.emptystring import CompareToEmptyStringChecker diff --git a/pylint/test/extensions/test_overlapping_exceptions.py b/pylint/test/extensions/test_overlapping_exceptions.py index 44b98515a8..d001b20532 100644 --- a/pylint/test/extensions/test_overlapping_exceptions.py +++ b/pylint/test/extensions/test_overlapping_exceptions.py @@ -4,13 +4,13 @@ """Tests for the pylint checker in :mod:`pylint.extensions.overlapping_exceptions """ +from os.path import dirname, join from sys import version_info -from os.path import join, dirname - -from pylint.extensions.overlapping_exceptions import OverlappingExceptionsChecker import pytest +from pylint.extensions.overlapping_exceptions import OverlappingExceptionsChecker + @pytest.fixture(scope='module') def checker(checker): diff --git a/pylint/test/extensions/test_redefined.py b/pylint/test/extensions/test_redefined.py index f4e2c79d6f..85871b064f 100644 --- a/pylint/test/extensions/test_redefined.py +++ b/pylint/test/extensions/test_redefined.py @@ -14,7 +14,6 @@ from pylint.extensions.redefined_variable_type import MultipleTypesChecker from pylint.lint import fix_import_path - EXPECTED = [ 'Redefinition of self.var1 type from int to float', 'Redefinition of var type from int to str', diff --git a/pylint/test/test_func.py b/pylint/test/test_func.py index 0d9694545d..1566df9928 100644 --- a/pylint/test/test_func.py +++ b/pylint/test/test_func.py @@ -13,11 +13,11 @@ """functional/non regression tests for pylint""" -import sys import re +import sys +from os.path import abspath, dirname, join import pytest -from os.path import abspath, dirname, join from pylint.testutils import _get_tests_info, linter diff --git a/pylint/test/test_functional.py b/pylint/test/test_functional.py index 576cd0d560..ae673bd135 100644 --- a/pylint/test/test_functional.py +++ b/pylint/test/test_functional.py @@ -12,24 +12,21 @@ # For details: https://github.com/PyCQA/pylint/blob/master/COPYING """Functional full-module tests for PyLint.""" -import csv import collections +import csv import io import operator import os +import platform import re import sys -import platform +import pytest import six from six.moves import configparser -import pytest +from pylint import checkers, interfaces, lint, reporters -from pylint import checkers -from pylint import interfaces -from pylint import lint -from pylint import reporters class test_dialect(csv.excel): if sys.version_info[0] < 3: diff --git a/pylint/test/test_import_graph.py b/pylint/test/test_import_graph.py index 218164565d..d7b0096934 100644 --- a/pylint/test/test_import_graph.py +++ b/pylint/test/test_import_graph.py @@ -14,10 +14,9 @@ import pytest -from pylint.checkers import initialize, imports -from pylint.lint import PyLinter - import pylint.testutils as testutils +from pylint.checkers import imports, initialize +from pylint.lint import PyLinter @pytest.fixture diff --git a/pylint/test/test_regr.py b/pylint/test/test_regr.py index c3e0260153..65e66eb20e 100644 --- a/pylint/test/test_regr.py +++ b/pylint/test/test_regr.py @@ -14,17 +14,16 @@ to be incorporated in the automatic functional test framework """ -import sys import os +import sys from os.path import abspath, dirname, join +import astroid import pytest -import astroid import pylint.testutils as testutils from pylint import epylint - REGR_DATA = join(dirname(abspath(__file__)), "regrtest_data") sys.path.insert(1, REGR_DATA) diff --git a/pylint/test/test_self.py b/pylint/test/test_self.py index 8fa262f17e..1ef4d2f9b9 100644 --- a/pylint/test/test_self.py +++ b/pylint/test/test_self.py @@ -20,25 +20,26 @@ # 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 +import configparser import contextlib import json +import os import re +import subprocess import sys -import os -from os.path import join, dirname, abspath import tempfile import textwrap -import configparser from io import StringIO -import subprocess +from os.path import abspath, dirname, join from unittest import mock +import pytest + +from pylint import utils from pylint.lint import Run from pylint.reporters import BaseReporter -from pylint.reporters.text import * from pylint.reporters.json import JSONReporter -import pytest -from pylint import utils +from pylint.reporters.text import * HERE = abspath(dirname(__file__)) diff --git a/pylint/test/unittest_checker_base.py b/pylint/test/unittest_checker_base.py index a1607cf8d1..1feae5b7ac 100644 --- a/pylint/test/unittest_checker_base.py +++ b/pylint/test/unittest_checker_base.py @@ -22,6 +22,7 @@ import unittest import astroid + from pylint.checkers import base from pylint.testutils import CheckerTestCase, Message, set_config diff --git a/pylint/test/unittest_checker_classes.py b/pylint/test/unittest_checker_classes.py index 1bf929b71c..3925cc36d7 100644 --- a/pylint/test/unittest_checker_classes.py +++ b/pylint/test/unittest_checker_classes.py @@ -10,6 +10,7 @@ """Unit tests for the variables checker.""" import astroid + from pylint.checkers import classes from pylint.testutils import CheckerTestCase, Message, set_config diff --git a/pylint/test/unittest_checker_format.py b/pylint/test/unittest_checker_format.py index 8811b08229..c47b4e54bf 100644 --- a/pylint/test/unittest_checker_format.py +++ b/pylint/test/unittest_checker_format.py @@ -24,18 +24,15 @@ from __future__ import unicode_literals -import tokenize import os import tempfile +import tokenize import astroid +from pylint import lint, reporters from pylint.checkers.format import * -from pylint import reporters -from pylint import lint - - -from pylint.testutils import CheckerTestCase, Message, set_config, _tokenize_str +from pylint.testutils import CheckerTestCase, Message, _tokenize_str, set_config class TestMultiStatementLine(CheckerTestCase): diff --git a/pylint/test/unittest_checker_imports.py b/pylint/test/unittest_checker_imports.py index b5961b26de..898be4ef59 100644 --- a/pylint/test/unittest_checker_imports.py +++ b/pylint/test/unittest_checker_imports.py @@ -14,9 +14,10 @@ import os import astroid + from pylint.checkers import imports -from pylint.testutils import CheckerTestCase, Message, set_config from pylint.interfaces import UNDEFINED +from pylint.testutils import CheckerTestCase, Message, set_config REGR_DATA = os.path.join(os.path.dirname(__file__), "regrtest_data", "") diff --git a/pylint/test/unittest_checker_misc.py b/pylint/test/unittest_checker_misc.py index 3fe49628d7..b72b7acaa2 100644 --- a/pylint/test/unittest_checker_misc.py +++ b/pylint/test/unittest_checker_misc.py @@ -13,7 +13,7 @@ """Tests for the misc checker.""" from pylint.checkers import misc -from pylint.testutils import CheckerTestCase, Message, set_config, _tokenize_str +from pylint.testutils import CheckerTestCase, Message, _tokenize_str, set_config class TestFixme(CheckerTestCase): diff --git a/pylint/test/unittest_checker_python3.py b/pylint/test/unittest_checker_python3.py index bc4c346b70..417bc2bb6b 100644 --- a/pylint/test/unittest_checker_python3.py +++ b/pylint/test/unittest_checker_python3.py @@ -20,14 +20,12 @@ import sys import textwrap -import pytest - import astroid +import pytest from pylint import testutils from pylint.checkers import python3 as checker -from pylint.interfaces import INFERENCE_FAILURE, INFERENCE - +from pylint.interfaces import INFERENCE, INFERENCE_FAILURE # Decorator for any tests that will fail under Python 3 python2_only = pytest.mark.skipif(sys.version_info[0] > 2, reason="Python 2 only") diff --git a/pylint/test/unittest_checker_similar.py b/pylint/test/unittest_checker_similar.py index f2cd901a83..a84883372d 100644 --- a/pylint/test/unittest_checker_similar.py +++ b/pylint/test/unittest_checker_similar.py @@ -10,10 +10,10 @@ # For details: https://github.com/PyCQA/pylint/blob/master/COPYING import sys -from os.path import join, dirname, abspath from contextlib import redirect_stdout - from io import StringIO +from os.path import abspath, dirname, join + import pytest from pylint.checkers import similar diff --git a/pylint/test/unittest_checker_spelling.py b/pylint/test/unittest_checker_spelling.py index f6b5ec2eda..8abd85577a 100644 --- a/pylint/test/unittest_checker_spelling.py +++ b/pylint/test/unittest_checker_spelling.py @@ -12,12 +12,11 @@ """Unittest for the spelling checker.""" -import pytest - import astroid +import pytest from pylint.checkers import spelling -from pylint.testutils import CheckerTestCase, Message, set_config, _tokenize_str +from pylint.testutils import CheckerTestCase, Message, _tokenize_str, set_config # try to create enchant dictionary try: diff --git a/pylint/test/unittest_checker_stdlib.py b/pylint/test/unittest_checker_stdlib.py index f425b0f99b..55c3dc8dc1 100644 --- a/pylint/test/unittest_checker_stdlib.py +++ b/pylint/test/unittest_checker_stdlib.py @@ -11,8 +11,8 @@ import astroid from pylint.checkers import stdlib -from pylint.testutils import CheckerTestCase, Message from pylint.interfaces import UNDEFINED +from pylint.testutils import CheckerTestCase, Message @contextlib.contextmanager diff --git a/pylint/test/unittest_checker_strings.py b/pylint/test/unittest_checker_strings.py index 4cac737479..bc4fce6f93 100644 --- a/pylint/test/unittest_checker_strings.py +++ b/pylint/test/unittest_checker_strings.py @@ -9,7 +9,6 @@ from pylint.checkers import strings from pylint.testutils import CheckerTestCase, Message - TEST_TOKENS = ( '"X"', "'X'", diff --git a/pylint/test/unittest_checker_typecheck.py b/pylint/test/unittest_checker_typecheck.py index 71fa7e82b5..a418087c2c 100644 --- a/pylint/test/unittest_checker_typecheck.py +++ b/pylint/test/unittest_checker_typecheck.py @@ -16,9 +16,8 @@ """Unittest for the type checker.""" import sys -import pytest - import astroid +import pytest from pylint.checkers import typecheck from pylint.testutils import CheckerTestCase, Message, set_config diff --git a/pylint/test/unittest_checker_variables.py b/pylint/test/unittest_checker_variables.py index a9b590a0a3..299a550d26 100644 --- a/pylint/test/unittest_checker_variables.py +++ b/pylint/test/unittest_checker_variables.py @@ -13,15 +13,15 @@ # For details: https://github.com/PyCQA/pylint/blob/master/COPYING """Unit tests for the variables checker.""" -import sys import os import re +import sys import astroid from pylint.checkers import variables -from pylint.testutils import CheckerTestCase, linter, set_config, Message from pylint.interfaces import UNDEFINED +from pylint.testutils import CheckerTestCase, Message, linter, set_config class TestVariablesChecker(CheckerTestCase): diff --git a/pylint/test/unittest_checkers_utils.py b/pylint/test/unittest_checkers_utils.py index 3471912c3b..7552d8c7ce 100644 --- a/pylint/test/unittest_checkers_utils.py +++ b/pylint/test/unittest_checkers_utils.py @@ -12,9 +12,9 @@ """Tests for the pylint.checkers.utils module.""" import astroid +import pytest from pylint.checkers import utils -import pytest @pytest.mark.parametrize( diff --git a/pylint/test/unittest_config.py b/pylint/test/unittest_config.py index 17e48d5a8a..ca36c00ebd 100644 --- a/pylint/test/unittest_config.py +++ b/pylint/test/unittest_config.py @@ -12,9 +12,9 @@ import re import sre_constants -from pylint import config import pytest +from pylint import config RE_PATTERN_TYPE = getattr(re, "Pattern", getattr(re, "_pattern_type", None)) diff --git a/pylint/test/unittest_pyreverse_diadefs.py b/pylint/test/unittest_pyreverse_diadefs.py index 6414979053..0659a5b11a 100644 --- a/pylint/test/unittest_pyreverse_diadefs.py +++ b/pylint/test/unittest_pyreverse_diadefs.py @@ -13,13 +13,11 @@ unit test for the extensions.diadefslib modules """ -import pytest - import astroid +import pytest -from pylint.pyreverse.inspector import Linker from pylint.pyreverse.diadefslib import * - +from pylint.pyreverse.inspector import Linker from unittest_pyreverse_writer import Config, get_project diff --git a/pylint/test/unittest_pyreverse_inspector.py b/pylint/test/unittest_pyreverse_inspector.py index f87bddbfde..c4433ba517 100644 --- a/pylint/test/unittest_pyreverse_inspector.py +++ b/pylint/test/unittest_pyreverse_inspector.py @@ -9,11 +9,9 @@ """ import os -import pytest - import astroid -from astroid import nodes -from astroid import bases +import pytest +from astroid import bases, nodes from pylint.pyreverse import inspector from unittest_pyreverse_writer import get_project diff --git a/pylint/test/unittest_pyreverse_writer.py b/pylint/test/unittest_pyreverse_writer.py index cf2e915b0f..01654024ae 100644 --- a/pylint/test/unittest_pyreverse_writer.py +++ b/pylint/test/unittest_pyreverse_writer.py @@ -15,17 +15,16 @@ """ -import os import codecs +import os from difflib import unified_diff import pytest -from pylint.pyreverse.inspector import Linker, project_from_files from pylint.pyreverse.diadefslib import DefaultDiadefGenerator, DiadefsHandler -from pylint.pyreverse.writer import DotWriter +from pylint.pyreverse.inspector import Linker, project_from_files from pylint.pyreverse.utils import get_visibility - +from pylint.pyreverse.writer import DotWriter _DEFAULTS = { "all_ancestors": None, diff --git a/pylint/test/unittest_reporters_json.py b/pylint/test/unittest_reporters_json.py index ba78538874..f3424ab669 100644 --- a/pylint/test/unittest_reporters_json.py +++ b/pylint/test/unittest_reporters_json.py @@ -11,11 +11,10 @@ """Test for the JSON reporter.""" import json - from io import StringIO -from pylint.lint import PyLinter from pylint import checkers +from pylint.lint import PyLinter from pylint.reporters.json import JSONReporter diff --git a/pylint/test/unittest_reporting.py b/pylint/test/unittest_reporting.py index 88f2ce4d52..a374e1e3ea 100644 --- a/pylint/test/unittest_reporting.py +++ b/pylint/test/unittest_reporting.py @@ -11,14 +11,14 @@ # For details: https://github.com/PyCQA/pylint/blob/master/COPYING import warnings - from io import StringIO -from pylint.lint import PyLinter -from pylint import checkers -from pylint.reporters.text import TextReporter, ParseableTextReporter import pytest +from pylint import checkers +from pylint.lint import PyLinter +from pylint.reporters.text import ParseableTextReporter, TextReporter + @pytest.fixture(scope="module") def reporter(reporter): diff --git a/pylint/test/utils/unittest_utils.py b/pylint/test/utils/unittest_utils.py index 6ad932dc61..8ed2f26c92 100644 --- a/pylint/test/utils/unittest_utils.py +++ b/pylint/test/utils/unittest_utils.py @@ -22,8 +22,8 @@ import astroid -from pylint.utils import utils, ASTWalker from pylint.checkers.utils import check_messages, get_node_last_lineno +from pylint.utils import ASTWalker, utils class TestASTWalker(object): diff --git a/pylint/testutils.py b/pylint/testutils.py index 17ded416a8..d47f67dd04 100644 --- a/pylint/testutils.py +++ b/pylint/testutils.py @@ -25,22 +25,22 @@ import collections import contextlib import functools -from glob import glob import os -from os import linesep, getcwd, sep -from os.path import abspath, basename, dirname, join, splitext import sys import tempfile import tokenize - +from glob import glob from io import StringIO +from os import getcwd, linesep, sep +from os.path import abspath, basename, dirname, join, splitext import astroid + from pylint import checkers -from pylint.utils import ASTWalker -from pylint.reporters import BaseReporter from pylint.interfaces import IReporter from pylint.lint import PyLinter +from pylint.reporters import BaseReporter +from pylint.utils import ASTWalker # Utils diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index 1b29540782..51b12f75dd 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -41,10 +41,10 @@ main pylint class """ +from pylint.utils.ast_walker import ASTWalker from pylint.utils.constants import OPTION_RGX, PY_EXTS from pylint.utils.file_state import FileState from pylint.utils.normalize_text import normalize_text -from pylint.utils.ast_walker import ASTWalker from pylint.utils.reports_handler_mix_in import ReportsHandlerMixIn from pylint.utils.utils import ( _basename_in_blacklist_re, diff --git a/pylint/utils/file_state.py b/pylint/utils/file_state.py index 46180833a9..2bc1662aa7 100644 --- a/pylint/utils/file_state.py +++ b/pylint/utils/file_state.py @@ -6,6 +6,7 @@ import collections from astroid import nodes + from pylint.message.constants import MSG_STATE_SCOPE_MODULE from pylint.utils.warning_scope import WarningScope diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index fb8e1ef10a..3147ac847d 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -13,8 +13,9 @@ from os.path import basename, dirname, exists, isdir, join, normpath, splitext from astroid import Module, modutils -from pylint.utils.constants import PY_EXTS + from pylint.message.constants import MSG_TYPES, MSG_TYPES_LONG +from pylint.utils.constants import PY_EXTS from pylint.utils.normalize_text import normalize_text diff --git a/setup.py b/setup.py index a7b5e4138c..77e0e2fff7 100644 --- a/setup.py +++ b/setup.py @@ -21,12 +21,15 @@ """Generic Setup script, takes package info from __pkginfo__.py file. """ from __future__ import absolute_import, print_function -__docformat__ = "restructuredtext en" import os -import sys import shutil -from os.path import isdir, exists, join +import sys +from distutils.command.build_py import build_py +from os.path import exists, isdir, join + +__docformat__ = "restructuredtext en" + try: from setuptools import setup @@ -39,7 +42,6 @@ USE_SETUPTOOLS = 0 easy_install_lib = None -from distutils.command.build_py import build_py base_dir = os.path.dirname(__file__) From 2d525d68249cf00c4dbb521e331e99b481a113c3 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 11:23:15 +0100 Subject: [PATCH 0051/5147] Style - Re-Apply black following the isort cleanup Change with isort triggered change with black. --- doc/conf.py | 127 ++++++++++---------- doc/exts/pylint_extensions.py | 75 ++++++------ doc/exts/pylint_features.py | 12 +- examples/custom.py | 32 ++--- examples/custom_raw.py | 22 ++-- pylint/__main__.py | 1 - pylint/checkers/similar.py | 4 +- pylint/checkers/spelling.py | 1 - pylint/config.py | 3 - pylint/extensions/mccabe.py | 1 - pylint/lint.py | 1 - pylint/pyreverse/__init__.py | 1 - pylint/test/extensions/test_check_docs.py | 6 +- pylint/test/test_functional.py | 4 +- pylint/test/unittest_checker_typecheck.py | 2 +- pylint/test/unittest_pyreverse_inspector.py | 2 +- setup.py | 123 ++++++++++--------- 17 files changed, 216 insertions(+), 201 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 16bfb7eb9f..f71c4d3415 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -25,204 +25,204 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.append(os.path.abspath('exts')) +sys.path.append(os.path.abspath("exts")) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ - 'pylint_features', - 'pylint_extensions', - 'sphinx.ext.autosectionlabel', - 'sphinx.ext.intersphinx', + "pylint_features", + "pylint_extensions", + "sphinx.ext.autosectionlabel", + "sphinx.ext.intersphinx", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'Pylint' +project = "Pylint" current_year = datetime.utcnow().year -copyright = '2003-{year}, Logilab, PyCQA and contributors'.format(year=current_year) +copyright = "2003-{year}, Logilab, PyCQA and contributors".format(year=current_year) # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'python_docs_theme' +html_theme = "python_docs_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { - 'collapsiblesidebar': True, - 'issues_url': 'https://github.com/pycqa/pylint/issues/new', - 'root_name': 'PyCQA', - 'root_url': 'http://meta.pycqa.org/en/latest/', + "collapsiblesidebar": True, + "issues_url": "https://github.com/pycqa/pylint/issues/new", + "root_name": "PyCQA", + "root_url": "http://meta.pycqa.org/en/latest/", } # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +# html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' smartquotes = False # Custom sidebar templates, maps document names to template names. html_sidebars = { - '**': [ - 'localtoc.html', - 'globaltoc.html', - 'relations.html', - 'sourcelink.html' - ] + "**": ["localtoc.html", "globaltoc.html", "relations.html", "sourcelink.html"] } # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'Pylintdoc' +htmlhelp_basename = "Pylintdoc" # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +# latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +# latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'Pylint.tex', 'Pylint Documentation', - 'Logilab, PyCQA and contributors', 'manual'), + ( + "index", + "Pylint.tex", + "Pylint Documentation", + "Logilab, PyCQA and contributors", + "manual", + ) ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output -------------------------------------------- @@ -230,13 +230,12 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'pylint', 'Pylint Documentation', - ['Logilab, PyCQA and contributors'], 1) + ("index", "pylint", "Pylint Documentation", ["Logilab, PyCQA and contributors"], 1) ] intersphinx_mapping = { - 'astroid': ('http://astroid.readthedocs.io/en/latest/', None), - 'python': ('https://docs.python.org/3', None), + "astroid": ("http://astroid.readthedocs.io/en/latest/", None), + "python": ("https://docs.python.org/3", None), } # Prevent label issues due to colliding section names diff --git a/doc/exts/pylint_extensions.py b/doc/exts/pylint_extensions.py index 2e1d821635..dd660767c9 100755 --- a/doc/exts/pylint_extensions.py +++ b/doc/exts/pylint_extensions.py @@ -18,28 +18,27 @@ # 1) They are deprecated, why document them moving forward? # 2) We can't load the deprecated module and the newly renamed module at the # same time without getting naming conflicts -DEPRECATED_MODULES = [ - 'check_docs', # ==> docparams -] +DEPRECATED_MODULES = ["check_docs"] # ==> docparams + def builder_inited(app): """Output full documentation in ReST format for all extension modules""" # PACKAGE/docs/exts/pylint_extensions.py --> PACKAGE/ base_path = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + ) # PACKAGE/ --> PACKAGE/pylint/extensions - ext_path = os.path.join(base_path, 'pylint', 'extensions') + ext_path = os.path.join(base_path, "pylint", "extensions") modules = [] doc_files = {} for filename in os.listdir(ext_path): name, ext = os.path.splitext(filename) - if name[0] == '_' or name in DEPRECATED_MODULES: + if name[0] == "_" or name in DEPRECATED_MODULES: continue - if ext == '.py': - modules.append('pylint.extensions.%s' % name) - elif ext == '.rst': - doc_files['pylint.extensions.' + name] = os.path.join(ext_path, - filename) + if ext == ".py": + modules.append("pylint.extensions.%s" % name) + elif ext == ".rst": + doc_files["pylint.extensions." + name] = os.path.join(ext_path, filename) modules.sort() if not modules: sys.exit("No Pylint extensions found?") @@ -47,29 +46,35 @@ def builder_inited(app): linter = PyLinter() linter.load_plugin_modules(modules) - extensions_doc = os.path.join(base_path, 'doc', 'technical_reference', 'extensions.rst') - with open(extensions_doc, 'w') as stream: + extensions_doc = os.path.join( + base_path, "doc", "technical_reference", "extensions.rst" + ) + with open(extensions_doc, "w") as stream: stream.write("Optional Pylint checkers in the extensions module\n") stream.write("=================================================\n\n") stream.write("Pylint provides the following optional plugins:\n\n") for module in modules: stream.write("- :ref:`{}`\n".format(module)) stream.write("\n") - stream.write("You can activate any or all of these extensions " - "by adding a ``load-plugins`` line to the ``MASTER`` " - "section of your ``.pylintrc``, for example::\n") - stream.write("\n load-plugins=pylint.extensions.docparams," - "pylint.extensions.docstyle\n\n") + stream.write( + "You can activate any or all of these extensions " + "by adding a ``load-plugins`` line to the ``MASTER`` " + "section of your ``.pylintrc``, for example::\n" + ) + stream.write( + "\n load-plugins=pylint.extensions.docparams," + "pylint.extensions.docstyle\n\n" + ) by_module = get_plugins_info(linter, doc_files) for module, info in sorted(by_module.items()): - linter._print_checker_doc(info['name'], info, stream=stream) + linter._print_checker_doc(info["name"], info, stream=stream) def get_plugins_info(linter, doc_files): by_module = {} for checker in linter.get_checkers(): - if checker.name == 'master': + if checker.name == "master": continue module = checker.__module__ # Plugins only - skip over core checkers @@ -80,32 +85,32 @@ def get_plugins_info(linter, doc_files): doc = "" doc_file = doc_files.get(module) if doc_file: - with open(doc_file, 'r') as f: + with open(doc_file, "r") as f: doc = f.read() try: - by_module[module]['options'] += checker.options_and_values() - by_module[module]['msgs'].update(checker.msgs) - by_module[module]['reports'] += checker.reports - by_module[module]['doc'] += doc - by_module[module]['name'] += checker.name - by_module[module]['module'] += module + by_module[module]["options"] += checker.options_and_values() + by_module[module]["msgs"].update(checker.msgs) + by_module[module]["reports"] += checker.reports + by_module[module]["doc"] += doc + by_module[module]["name"] += checker.name + by_module[module]["module"] += module except KeyError: by_module[module] = { - 'options': list(checker.options_and_values()), - 'msgs': dict(checker.msgs), - 'reports': list(checker.reports), - 'doc': doc, - 'name': checker.name, - 'module': module, + "options": list(checker.options_and_values()), + "msgs": dict(checker.msgs), + "reports": list(checker.reports), + "doc": doc, + "name": checker.name, + "module": module, } return by_module def setup(app): - app.connect('builder-inited', builder_inited) - return {'version': sphinx.__display_version__} + app.connect("builder-inited", builder_inited) + return {"version": sphinx.__display_version__} if __name__ == "__main__": diff --git a/doc/exts/pylint_features.py b/doc/exts/pylint_features.py index 2ff0e52a76..4803efc9ac 100755 --- a/doc/exts/pylint_features.py +++ b/doc/exts/pylint_features.py @@ -15,12 +15,13 @@ def builder_inited(app): # PACKAGE/docs/exts/pylint_extensions.py --> PACKAGE/ base_path = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + ) linter = PyLinter() linter.load_default_plugins() - features = os.path.join(base_path, 'doc', 'technical_reference', 'features.rst') - with open(features, 'w') as stream: + features = os.path.join(base_path, "doc", "technical_reference", "features.rst") + with open(features, "w") as stream: stream.write("Pylint features\n") stream.write("===============\n\n") stream.write(".. generated by pylint --full-documentation\n\n") @@ -28,8 +29,9 @@ def builder_inited(app): def setup(app): - app.connect('builder-inited', builder_inited) - return {'version': sphinx.__display_version__} + app.connect("builder-inited", builder_inited) + return {"version": sphinx.__display_version__} + if __name__ == "__main__": builder_inited(None) diff --git a/examples/custom.py b/examples/custom.py index 039b018b8b..7ee97bb5fd 100644 --- a/examples/custom.py +++ b/examples/custom.py @@ -14,7 +14,7 @@ class MyAstroidChecker(BaseChecker): __implements__ = IAstroidChecker # The name defines a custom section of the config for this checker. - name = 'custom' + name = "custom" # The priority indicates the order that pylint will run the checkers. priority = -1 # This class variable declares the messages (ie the warnings and errors) @@ -24,9 +24,7 @@ class MyAstroidChecker(BaseChecker): # a unique symbol that identifies the message, # and a detailed help message # that will be included in the documentation. - 'W0001': ('Message that will be emitted', - 'message-symbol', - 'Message help') + "W0001": ("Message that will be emitted", "message-symbol", "Message help") } # This class variable declares the options # that are configurable by the user. @@ -35,12 +33,16 @@ class MyAstroidChecker(BaseChecker): # and in config files, and a dictionary of arguments # (similar to those to those to # argparse.ArgumentParser.add_argument). - ('store-locals-indicator', - {'default': 'properties', - 'help': ('The expression name that indicates that the locals should ' - 'be stored'), - }, - ), + ( + "store-locals-indicator", + { + "default": "properties", + "help": ( + "The expression name that indicates that the locals should " + "be stored" + ), + }, + ), ) def visit_call(self, node): @@ -51,10 +53,12 @@ def visit_call(self, node): :param node: The node to check. :type node: astroid.node_classes.Call """ - if not (isinstance(node.func, astroid.Attribute) - and isinstance(node.func.expr, astroid.Name) - and node.func.expr.name == self.config.store_locals_indicator - and node.func.attrname == 'create'): + if not ( + isinstance(node.func, astroid.Attribute) + and isinstance(node.func.expr, astroid.Name) + and node.func.expr.name == self.config.store_locals_indicator + and node.func.attrname == "create" + ): return in_class = node.frame() for param in node.args: diff --git a/examples/custom_raw.py b/examples/custom_raw.py index fb28d49311..63e4aeb8ee 100644 --- a/examples/custom_raw.py +++ b/examples/custom_raw.py @@ -9,12 +9,17 @@ class MyRawChecker(BaseChecker): __implements__ = IRawChecker - name = 'custom_raw' - msgs = {'W9901': ('use \\ for line continuation', - 'backslash-line-continuation', - ('Used when a \\ is used for a line continuation instead' - ' of using triple quoted string or parenthesis.')), - } + name = "custom_raw" + msgs = { + "W9901": ( + "use \\ for line continuation", + "backslash-line-continuation", + ( + "Used when a \\ is used for a line continuation instead" + " of using triple quoted string or parenthesis." + ), + ) + } options = () def process_module(self, node): @@ -24,9 +29,8 @@ def process_module(self, node): """ with node.stream() as stream: for (lineno, line) in enumerate(stream): - if line.rstrip().endswith('\\'): - self.add_message('backslash-line-continuation', - line=lineno) + if line.rstrip().endswith("\\"): + self.add_message("backslash-line-continuation", line=lineno) def register(linter): diff --git a/pylint/__main__.py b/pylint/__main__.py index 7eca119293..e12309b4da 100644 --- a/pylint/__main__.py +++ b/pylint/__main__.py @@ -1,4 +1,3 @@ - # 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 diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py index d79785d667..f65f533cd9 100644 --- a/pylint/checkers/similar.py +++ b/pylint/checkers/similar.py @@ -109,7 +109,7 @@ def _display_sims(self, sims): % ( nb_total_lignes, nb_lignes_dupliquees, - nb_lignes_dupliquees * 100. / nb_total_lignes, + nb_lignes_dupliquees * 100.0 / nb_total_lignes, ) ) @@ -387,7 +387,7 @@ def close(self): 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. / total + stats["percent_duplicated_lines"] = total and duplicated * 100.0 / total def register(linter): diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py index f1952e89fc..c1a0eb2de3 100644 --- a/pylint/checkers/spelling.py +++ b/pylint/checkers/spelling.py @@ -47,7 +47,6 @@ class Chunker: # type: ignore pass - if enchant is not None: br = enchant.Broker() dicts = br.list_dicts() diff --git a/pylint/config.py b/pylint/config.py index a913f86199..8b0373f612 100644 --- a/pylint/config.py +++ b/pylint/config.py @@ -57,9 +57,6 @@ # need a cleanup. It could be completely reengineered as well. - - - USER_HOME = os.path.expanduser("~") if "PYLINTHOME" in os.environ: PYLINT_HOME = os.environ["PYLINTHOME"] diff --git a/pylint/extensions/mccabe.py b/pylint/extensions/mccabe.py index 69bc1775ba..18baa2601b 100644 --- a/pylint/extensions/mccabe.py +++ b/pylint/extensions/mccabe.py @@ -11,7 +11,6 @@ from mccabe import PathGraph as Mccabe_PathGraph from mccabe import PathGraphingAstVisitor as Mccabe_PathGraphingAstVisitor - from pylint import checkers from pylint.checkers.utils import check_messages from pylint.interfaces import HIGH, IAstroidChecker diff --git a/pylint/lint.py b/pylint/lint.py index d02410fa08..25129ecb1d 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -86,7 +86,6 @@ multiprocessing = None # type: ignore - MANAGER = astroid.MANAGER diff --git a/pylint/pyreverse/__init__.py b/pylint/pyreverse/__init__.py index 8a12c4fec5..9ca1da5c6a 100644 --- a/pylint/pyreverse/__init__.py +++ b/pylint/pyreverse/__init__.py @@ -1,4 +1,3 @@ - # 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 diff --git a/pylint/test/extensions/test_check_docs.py b/pylint/test/extensions/test_check_docs.py index 63825c415f..1eacc93878 100644 --- a/pylint/test/extensions/test_check_docs.py +++ b/pylint/test/extensions/test_check_docs.py @@ -762,7 +762,7 @@ class ClassFoo(object): '''docstring foo :param y: bla - + missing constructor parameter documentation ''' @@ -794,7 +794,7 @@ class ClassFoo(object): Args: y: bla - + missing constructor parameter documentation ''' @@ -828,7 +828,7 @@ class ClassFoo(object): ---------- y: bla - + missing constructor parameter documentation ''' diff --git a/pylint/test/test_functional.py b/pylint/test/test_functional.py index ae673bd135..1280631652 100644 --- a/pylint/test/test_functional.py +++ b/pylint/test/test_functional.py @@ -22,10 +22,10 @@ import sys import pytest -import six -from six.moves import configparser +import six from pylint import checkers, interfaces, lint, reporters +from six.moves import configparser class test_dialect(csv.excel): diff --git a/pylint/test/unittest_checker_typecheck.py b/pylint/test/unittest_checker_typecheck.py index a418087c2c..7bce396c5c 100644 --- a/pylint/test/unittest_checker_typecheck.py +++ b/pylint/test/unittest_checker_typecheck.py @@ -50,7 +50,7 @@ def test_no_member_in_getattr(self): node = astroid.extract_node( """ import optparse - optparse.THIS_does_not_EXIST + optparse.THIS_does_not_EXIST """ ) with self.assertAddsMessages( diff --git a/pylint/test/unittest_pyreverse_inspector.py b/pylint/test/unittest_pyreverse_inspector.py index c4433ba517..fe5df9b981 100644 --- a/pylint/test/unittest_pyreverse_inspector.py +++ b/pylint/test/unittest_pyreverse_inspector.py @@ -94,7 +94,7 @@ class MyIFace(Interface): pass class AnotherIFace(Interface): pass class Concrete0(object): __implements__ = MyIFace - class Concrete1: + class Concrete1: __implements__ = (MyIFace, AnotherIFace) class Concrete2: __implements__ = (MyIFace, AnotherIFace) diff --git a/setup.py b/setup.py index 77e0e2fff7..2e76e4878b 100644 --- a/setup.py +++ b/setup.py @@ -35,36 +35,37 @@ from setuptools import setup from setuptools.command import easy_install as easy_install_lib from setuptools.command import install_lib + USE_SETUPTOOLS = 1 except ImportError: from distutils.core import setup from distutils.command import install_lib + USE_SETUPTOOLS = 0 easy_install_lib = None - base_dir = os.path.dirname(__file__) __pkginfo__ = {} with open(os.path.join(base_dir, "pylint", "__pkginfo__.py")) as f: exec(f.read(), __pkginfo__) -modname = __pkginfo__['modname'] -distname = __pkginfo__.get('distname', modname) -scripts = __pkginfo__.get('scripts', []) -data_files = __pkginfo__.get('data_files', None) -include_dirs = __pkginfo__.get('include_dirs', []) -ext_modules = __pkginfo__.get('ext_modules', None) -install_requires = __pkginfo__.get('install_requires', None) -dependency_links = __pkginfo__.get('dependency_links', []) -extras_require = __pkginfo__.get('extras_require', {}) - -readme_path = join(base_dir, 'README.rst') +modname = __pkginfo__["modname"] +distname = __pkginfo__.get("distname", modname) +scripts = __pkginfo__.get("scripts", []) +data_files = __pkginfo__.get("data_files", None) +include_dirs = __pkginfo__.get("include_dirs", []) +ext_modules = __pkginfo__.get("ext_modules", None) +install_requires = __pkginfo__.get("install_requires", None) +dependency_links = __pkginfo__.get("dependency_links", []) +extras_require = __pkginfo__.get("extras_require", {}) + +readme_path = join(base_dir, "README.rst") if exists(readme_path): with open(readme_path) as stream: long_description = stream.read() else: - long_description = '' + long_description = "" def ensure_scripts(linux_scripts): @@ -72,8 +73,9 @@ def ensure_scripts(linux_scripts): (taken from 4Suite) """ from distutils import util - if util.get_platform()[:3] == 'win': - return linux_scripts + [script + '.bat' for script in linux_scripts] + + if util.get_platform()[:3] == "win": + return linux_scripts + [script + ".bat" for script in linux_scripts] return linux_scripts @@ -83,9 +85,9 @@ def get_packages(directory, prefix): for package in os.listdir(directory): absfile = join(directory, package) if isdir(absfile): - if exists(join(absfile, '__init__.py')): + if exists(join(absfile, "__init__.py")): if prefix: - result.append('%s.%s' % (prefix, package)) + result.append("%s.%s" % (prefix, package)) else: result.append(package) result += get_packages(absfile, result[-1]) @@ -93,7 +95,7 @@ def get_packages(directory, prefix): def _filter_tests(files): - testdir = join('pylint', 'test') + testdir = join("pylint", "test") return [f for f in files if testdir not in f] @@ -101,6 +103,7 @@ class MyInstallLib(install_lib.install_lib): """extend install_lib command to handle package __init__.py and include_dirs variable if necessary """ + def run(self): """overridden from install_lib class""" install_lib.install_lib.run(self) @@ -109,12 +112,13 @@ def run(self): for directory in include_dirs: dest = join(self.install_dir, directory) if sys.version_info >= (3, 0): - exclude = {'invalid_encoded_data*', 'unknown_encoding*'} + exclude = {"invalid_encoded_data*", "unknown_encoding*"} else: exclude = set() shutil.rmtree(dest, ignore_errors=True) - shutil.copytree(directory, dest, - ignore=shutil.ignore_patterns(*exclude)) + shutil.copytree( + directory, dest, ignore=shutil.ignore_patterns(*exclude) + ) # override this since pip/easy_install attempt to byte compile test data # files, some of them being syntactically wrong by design, and this scares @@ -125,6 +129,7 @@ def byte_compile(self, files): if easy_install_lib: + class easy_install(easy_install_lib.easy_install): # override this since pip/easy_install attempt to byte compile # test data files, some of them being syntactically wrong by design, @@ -137,43 +142,47 @@ def byte_compile(self, files): def install(**kwargs): """setup entry point""" if USE_SETUPTOOLS: - if '--force-manifest' in sys.argv: - sys.argv.remove('--force-manifest') - packages = [modname] + get_packages(join(base_dir, 'pylint'), modname) + if "--force-manifest" in sys.argv: + sys.argv.remove("--force-manifest") + packages = [modname] + get_packages(join(base_dir, "pylint"), modname) if USE_SETUPTOOLS: if install_requires: - kwargs['install_requires'] = install_requires - kwargs['dependency_links'] = dependency_links - kwargs['entry_points'] = {'console_scripts': [ - 'pylint = pylint:run_pylint', - 'epylint = pylint:run_epylint', - 'pyreverse = pylint:run_pyreverse', - 'symilar = pylint:run_symilar', - ]} - kwargs['packages'] = packages - cmdclass = {'install_lib': MyInstallLib, - 'build_py': build_py} + kwargs["install_requires"] = install_requires + kwargs["dependency_links"] = dependency_links + kwargs["entry_points"] = { + "console_scripts": [ + "pylint = pylint:run_pylint", + "epylint = pylint:run_epylint", + "pyreverse = pylint:run_pyreverse", + "symilar = pylint:run_symilar", + ] + } + kwargs["packages"] = packages + cmdclass = {"install_lib": MyInstallLib, "build_py": build_py} if easy_install_lib: - cmdclass['easy_install'] = easy_install - return setup(name=distname, - version=__pkginfo__['version'], - license=__pkginfo__['license'], - description=__pkginfo__['description'], - long_description=long_description, - author=__pkginfo__['author'], - author_email=__pkginfo__['author_email'], - url=__pkginfo__['web'], - scripts=ensure_scripts(scripts), - classifiers=__pkginfo__['classifiers'], - data_files=data_files, - ext_modules=ext_modules, - cmdclass=cmdclass, - extras_require=extras_require, - test_suite='test', - python_requires='>=3.4.*', - setup_requires=['pytest-runner'], - tests_require=['pytest'], - **kwargs) - -if __name__ == '__main__': + cmdclass["easy_install"] = easy_install + return setup( + name=distname, + version=__pkginfo__["version"], + license=__pkginfo__["license"], + description=__pkginfo__["description"], + long_description=long_description, + author=__pkginfo__["author"], + author_email=__pkginfo__["author_email"], + url=__pkginfo__["web"], + scripts=ensure_scripts(scripts), + classifiers=__pkginfo__["classifiers"], + data_files=data_files, + ext_modules=ext_modules, + cmdclass=cmdclass, + extras_require=extras_require, + test_suite="test", + python_requires=">=3.4.*", + setup_requires=["pytest-runner"], + tests_require=["pytest"], + **kwargs + ) + + +if __name__ == "__main__": install() From bb6347e0e3a144651a0d42129b903d9dc523e566 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 11:14:14 +0100 Subject: [PATCH 0052/5147] Fix - C0412: Imports from package x are not grouped --- pylint/checkers/imports.py | 14 +++++++------- pylint/extensions/mccabe.py | 1 + pylint/test/test_functional.py | 5 +++-- pylint/testutils.py | 11 +++++------ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index 82ef94a079..6fc5400bcc 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -37,8 +37,6 @@ import astroid import isort -from astroid import are_exclusive, decorators -from astroid.modutils import get_module_part, is_standard_module from pylint.checkers import BaseChecker from pylint.checkers.utils import ( @@ -114,7 +112,7 @@ def _get_first_import(node, context, name, base, level, alias): break if found: break - if found and not are_exclusive(first, node): + if found and not astroid.are_exclusive(first, node): return first return None @@ -789,14 +787,16 @@ def _add_imported_module(self, node, importedmodname): base = os.path.splitext(os.path.basename(module_file))[0] try: - importedmodname = get_module_part(importedmodname, module_file) + importedmodname = astroid.modutils.get_module_part( + importedmodname, module_file + ) except ImportError: pass if context_name == importedmodname: self.add_message("import-self", node=node) - elif not is_standard_module(importedmodname): + elif not astroid.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 @@ -897,14 +897,14 @@ def _filter_dependencies_graph(self, internal): graph[importee].add(importer) return graph - @decorators.cached + @astroid.decorators.cached def _external_dependencies_info(self): """return cached external dependencies information or build and cache them """ return self._filter_dependencies_graph(internal=False) - @decorators.cached + @astroid.decorators.cached def _internal_dependencies_info(self): """return cached internal dependencies information or build and cache them diff --git a/pylint/extensions/mccabe.py b/pylint/extensions/mccabe.py index 18baa2601b..69bc1775ba 100644 --- a/pylint/extensions/mccabe.py +++ b/pylint/extensions/mccabe.py @@ -11,6 +11,7 @@ from mccabe import PathGraph as Mccabe_PathGraph from mccabe import PathGraphingAstVisitor as Mccabe_PathGraphingAstVisitor + from pylint import checkers from pylint.checkers.utils import check_messages from pylint.interfaces import HIGH, IAstroidChecker diff --git a/pylint/test/test_functional.py b/pylint/test/test_functional.py index 1280631652..bb4145c4d4 100644 --- a/pylint/test/test_functional.py +++ b/pylint/test/test_functional.py @@ -12,6 +12,7 @@ # For details: https://github.com/PyCQA/pylint/blob/master/COPYING """Functional full-module tests for PyLint.""" + import collections import csv import io @@ -22,11 +23,11 @@ import sys import pytest - import six -from pylint import checkers, interfaces, lint, reporters from six.moves import configparser +from pylint import checkers, interfaces, lint, reporters + class test_dialect(csv.excel): if sys.version_info[0] < 3: diff --git a/pylint/testutils.py b/pylint/testutils.py index d47f67dd04..53e395881f 100644 --- a/pylint/testutils.py +++ b/pylint/testutils.py @@ -25,13 +25,12 @@ import collections import contextlib import functools -import os import sys import tempfile import tokenize from glob import glob from io import StringIO -from os import getcwd, linesep, sep +from os import close, getcwd, linesep, remove, sep, write from os.path import abspath, basename, dirname, join, splitext import astroid @@ -286,14 +285,14 @@ def _create_tempfile(content=None): if content: if sys.version_info >= (3, 0): # erff - os.write(file_handle, bytes(content, "ascii")) + write(file_handle, bytes(content, "ascii")) else: - os.write(file_handle, content) + write(file_handle, content) try: yield tmp finally: - os.close(file_handle) - os.remove(tmp) + close(file_handle) + remove(tmp) @contextlib.contextmanager From fa31f356196f4b79d0bf9b4682c418e74761f3d0 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 11:34:10 +0100 Subject: [PATCH 0053/5147] Fix - An error added when applying isort/black everywhere --- pylint/test/extensions/data/docstring.py | 3 +-- pylint/test/extensions/test_docstyle.py | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pylint/test/extensions/data/docstring.py b/pylint/test/extensions/data/docstring.py index 5a2c3fe63b..8f34c657f8 100644 --- a/pylint/test/extensions/data/docstring.py +++ b/pylint/test/extensions/data/docstring.py @@ -2,7 +2,7 @@ def check_messages(*messages): - """ + """ docstring""" return messages @@ -38,4 +38,3 @@ def method6(self): def method7(self): u"""Test OK 3 with unicode string""" - diff --git a/pylint/test/extensions/test_docstyle.py b/pylint/test/extensions/test_docstyle.py index c4d3d881c2..cad929d318 100644 --- a/pylint/test/extensions/test_docstyle.py +++ b/pylint/test/extensions/test_docstyle.py @@ -8,7 +8,7 @@ """Tests for the pylint checker in :mod:`pylint.extensions.check_docstring """ -import os.path as osp +from os.path import abspath, dirname, join import pytest @@ -41,8 +41,7 @@ def checker(checker): def test_docstring_message(linter): - docstring_test = osp.join(osp.dirname(osp.abspath(__file__)), 'data', - 'docstring.py') + docstring_test = join(dirname(abspath(__file__)), 'data', 'docstring.py') linter.check([docstring_test]) msgs = linter.reporter.messages assert len(msgs) == 7 From c174aee91ba10dc041ddde6dbb20ed1f2e9f5bcb Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 11:53:13 +0100 Subject: [PATCH 0054/5147] Fix - A problem with comment handling in import Apparently black and isort do not behave the same. See : https://github.com/ambv/black/issues/251 --- pylint/test/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylint/test/conftest.py b/pylint/test/conftest.py index 985866893d..e2762091da 100644 --- a/pylint/test/conftest.py +++ b/pylint/test/conftest.py @@ -1,11 +1,11 @@ # pylint: disable=redefined-outer-name +# pylint: disable=no-name-in-module import os import pytest from pylint import checkers from pylint.lint import PyLinter -# pylint: disable=no-name-in-module from pylint.testutils import MinimalTestReporter From e6e6e2778b31868c0c5ba78f80f2e38225302fd6 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Mar 2019 21:53:48 +0100 Subject: [PATCH 0055/5147] Doc - Add isort in the contribution doc --- doc/development_guide/contribute.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/development_guide/contribute.rst b/doc/development_guide/contribute.rst index e74e606e2d..5bf74a17ce 100644 --- a/doc/development_guide/contribute.rst +++ b/doc/development_guide/contribute.rst @@ -95,7 +95,7 @@ your patch gets accepted. pytest pylint -k test_functional - * ``pylint`` uses black_ Python autoformatter for formatting its code. + * ``pylint`` uses black_ and isort_ Python autoformatter for formatting its code. We have a pre-commit hook which should take care of the autoformatting for you for when you are working on a patch. To enable it, do the following: @@ -165,6 +165,7 @@ current environment in order to have faster feedback. Run with:: .. _tox: http://tox.readthedocs.io/en/latest/ .. _pytest: http://pytest.readthedocs.io/en/latest/ .. _black: https://github.com/ambv/black +.. _isort: https://github.com/timothycrosley/isort .. _astroid: https://github.com/pycqa/astroid From 30b2ae713eb4453b305407772b29ea68d7c3d074 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 19 Mar 2019 20:44:59 +0100 Subject: [PATCH 0056/5147] Feat - Add an isort formatting check in the tox.ini Permit to verify the formatting of future commit. Following review see : https://github.com/PyCQA/pylint/pull/2805#discussion_r266793201 --- .pre-commit-config.yaml | 4 ++-- tox.ini | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1e553c91cf..11890aa26b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/ambv/black - rev: 18.9b0 + rev: 19.3b0 hooks: - id: black args: [--safe, --quiet] @@ -12,6 +12,6 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/pre-commit/mirrors-isort - rev: v4.3.13 + rev: v4.3.15 hooks: - id: isort diff --git a/tox.ini b/tox.ini index c6ee64354a..b539381d2e 100644 --- a/tox.ini +++ b/tox.ini @@ -12,8 +12,12 @@ commands = pylint -rn --rcfile={toxinidir}/pylintrc --load-plugins=pylint.extens [testenv:formatting] basepython = python3 -deps = black==18.9b0 -commands = black --check --exclude "functional|input|test/extension|test/regrtest_data|test/data" pylint +deps = + black==19.3b0 + isort==4.3.15 +commands = + black --check --exclude "functional|input|test/extension|test/regrtest_data|test/data" pylint + isort -rc pylint/ --check-only changedir = {toxinidir} [testenv:mypy] From a138a1a8f4bd1ff4ff5718c8b23a9b82c27aef3c Mon Sep 17 00:00:00 2001 From: yory8 <39745367+yory8@users.noreply.github.com> Date: Wed, 20 Mar 2019 08:41:16 +0000 Subject: [PATCH 0057/5147] Add new linter: dict-iter-missing-items Add a new linter to check against forgotten calls to `.items()` when iterating through a dictionary in a `for` loop. Close #2761 --- CONTRIBUTORS.txt | 3 +- ChangeLog | 5 +++- doc/whatsnew/2.4.rst | 6 +++- pylint/checkers/typecheck.py | 28 +++++++++++++++++++ .../functional/dict_iter_missing_items.py | 23 +++++++++++++++ .../functional/dict_iter_missing_items.txt | 1 + 6 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 pylint/test/functional/dict_iter_missing_items.py create mode 100644 pylint/test/functional/dict_iter_missing_items.txt diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 18ae9a83c0..cb37e62553 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -272,6 +272,8 @@ contributors: * Fantix King (UChicago): contributor +* Yory (yory8): contributor + * Thomas Hisch: contributor * Clément Pit-Claudel : contributor @@ -283,4 +285,3 @@ contributors: * Bluesheeptoken: contributor * Michael Scott Cuthbert: contributor - diff --git a/ChangeLog b/ChangeLog index 095f040f41..160dcaf1cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,10 @@ Release date: TBA This check is emitted when ``pylint`` finds a class variable that conflicts with a slot name, which would raise a ``ValueError`` at runtime. +* Added new check: dict-iter-missing-items (E1141) + + Close #2761 + * Fix issue with pylint name in output of python -m pylint --version Close #2764 @@ -52,7 +56,6 @@ Release date: TBA Close #2816 - What's New in Pylint 2.3.0? =========================== diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index 45a6b832a0..b96e0992ff 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -13,6 +13,11 @@ Summary -- Release highlights New checkers ============ +* We added a new check message ``dict-iter-missing-items``. + This is emitted when trying to iterate through a dict in a for loop without calling its .items() method. + + Closes #2761 + * We added a new check message ``missing-parentheses-for-call-in-test``. This is emitted in case a call to a function is made inside a test but it misses parentheses. @@ -28,7 +33,6 @@ New checkers __slots__ = ('first', 'second') first = 1 - Other Changes ============= diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index fa95d7cc4a..61aff8f93d 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -353,6 +353,12 @@ def _missing_member_hint(owner, attrname, distance_threshold, max_choices): "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", @@ -1522,6 +1528,28 @@ def visit_subscript(self, node): 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): """ diff --git a/pylint/test/functional/dict_iter_missing_items.py b/pylint/test/functional/dict_iter_missing_items.py new file mode 100644 index 0000000000..333f589908 --- /dev/null +++ b/pylint/test/functional/dict_iter_missing_items.py @@ -0,0 +1,23 @@ +# pylint: disable=invalid-name, consider-iterating-dictionary, missing-docstring, import-error +from unknown import Uninferable + +d = {1: 1, 2: 2} +l = [1, 2] +s1 = {1, 2} +s2 = {1, 2, 3} + +# Positive +for k, v in d: # [dict-iter-missing-items] + pass + +# Negative +for k, v in d.items(): + pass +for k in d.keys(): + pass +for i, v in enumerate(l): + pass +for i, v in s1.intersection(s2): + pass +for k, v in Uninferable: + pass diff --git a/pylint/test/functional/dict_iter_missing_items.txt b/pylint/test/functional/dict_iter_missing_items.txt new file mode 100644 index 0000000000..0e939ee33f --- /dev/null +++ b/pylint/test/functional/dict_iter_missing_items.txt @@ -0,0 +1 @@ +dict-iter-missing-items:10::Unpacking a dictionary in iteration without calling .items() From 83d2536a3a38f6f6123bd7e10aabd7eb0e716ba9 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 20 Mar 2019 09:42:05 +0100 Subject: [PATCH 0058/5147] Fix formatting error --- pylint/checkers/typecheck.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 61aff8f93d..bd38e52220 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -356,8 +356,7 @@ def _missing_member_hint(owner, attrname, distance_threshold, max_choices): "E1141": ( "Unpacking a dictionary in iteration without calling .items()", "dict-iter-missing-items", - "Emitted when trying to iterate through" - "a dict without calling .items()", + "Emitted when trying to iterate through a dict without calling .items()", ), "W1113": ( "Keyword argument before variable positional arguments list " @@ -1551,6 +1550,7 @@ def visit_for(self, node): self.add_message("dict-iter-missing-items", node=node) + class IterableChecker(BaseChecker): """ Checks for non-iterables used in an iterable context. From ec2eb68f8f9bcad56133b59d4c7e584b7db0f646 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 20 Mar 2019 10:45:12 +0100 Subject: [PATCH 0059/5147] Add Tidelift information --- README.rst | 22 +++++++++++++++--- doc/index.rst | 9 +++++-- ..._Logos_RGB_Tidelift_Shorthand_On-White.png | Bin 0 -> 4069 bytes ..._RGB_Tidelift_Shorthand_On-White_small.png | Bin 0 -> 7070 bytes doc/support.rst | 17 ++++++++++++++ 5 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png create mode 100644 doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png create mode 100644 doc/support.rst diff --git a/README.rst b/README.rst index 95a08bcedc..44b58311bf 100644 --- a/README.rst +++ b/README.rst @@ -24,6 +24,24 @@ README for Pylint - http://pylint.pycqa.org/ .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/ambv/black +.. |tideliftlogo| image:: doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png + :width: 75 + :height: 60 + :alt: Tidelift + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for pylint is available as part of the `Tidelift + Subscription`_. Tidelift gives software development teams a single source for + purchasing and maintaining their software, with professional grade assurances + from the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-pylint?utm_source=pypi-pylint&utm_medium=referral&utm_campaign=readme + + ====== Pylint ====== @@ -65,7 +83,7 @@ If you want to install from a source distribution, extract the tarball and run the following command :: python setup.py install - + Do make sure to do the same for astroid, which is used internally by pylint. @@ -113,5 +131,3 @@ The icon files are licensed under the `CC BY-SA 4.0 `_ - `doc/logo.svg `_ - - diff --git a/doc/index.rst b/doc/index.rst index 57e194eefb..1e70943d17 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -2,6 +2,12 @@ Pylint User Manual ================== +Pylint is a tool that checks for errors in Python code, tries to enforce a +coding standard and looks for code smells. It can also look for certain type +errors, it can recommend suggestions about how particular blocks can be +refactored and can offer you details about the code's complexity. + + .. toctree:: :maxdepth: 2 :titlesonly: @@ -16,6 +22,5 @@ Pylint User Manual faq backlinks + support whatsnew/index.rst - - diff --git a/doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png b/doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png new file mode 100644 index 0000000000000000000000000000000000000000..317dc4d9852df72ba34e10a6f61d1838cbbd969e GIT binary patch literal 4069 zcmeHKX;4$y626285oA+{C5WQL2QzE}vKV%kz!e2?31JbUA|g025fUUiY>rrh;Fz#z z06|dH3QI(hEkM{rKoG$YWc4YEhKLZ18;b7)bWFXc<5)HRd#Oq#_ttm5?$cj)pFYPo zyErPysmnnSqyRgu+X6udg7}M)0dK1PPxphLxxYGjghG&^w)iV`@PZu$f|SzWbq?DO z_C6ah4%Qe^kGo^BFn9}Wweq=KL%w~OQAe>k@xrzRt$Hh~5Uu+OjC)WnQn0o_>#pJ@ z&CP1g1H0nvX+fOgO{XT6ZcF{HwLbQa-z~z()!qhEDHB0ypF#d!iJC(uW`< z8j9v1AZ6g3_%UVR3D76&|7-5cpieG-^y!}gecJ3t$0dA{Fed?$ z3>$F#+aQUE0LQ<=^t~VnmB3O-pCp(hKw@Qoq?BLJoG+^+ekwPKb6_F&q)Jo=qm z0S5!yi4(~rvjy()rT6^M+0%JBUrT zucJ9Rt1jXo)%!c%FI%ikP(8O7X}?Sc2VSd2Y9lxNTQeMqZXD0o%T|@u*}KqYH`pb| zf37}|a6f^EQYNfol=j9TBA)01>CA7HyM5M{<(si|{m0_{%Ph3ry%P!YpeOsOABOcY z;*8ODG5J6p3bHFzCa@gZjiEUV4Jlv+iAE&Y3|mWYrkzpIV(He_p`zFHTlPkC>Ozh{ z>1O5#B>F;`IU*Y>?P_)+E@SD|hnX;R%8ln{*N)LMG^7y;o!;tF9rw%l+?si@oSelX zrNV_GC6nm9*JiMzH(0Q`*dNBigH5>_(F5h1gyzjrK(jYtCZ6hZU7wv%8E-gMveCrJ z#Bz1z6U@_YU$}FsE^2Vo0b;!UGvmEhdqs8&VYJAIs>KQ*?>FEtrP}U!od{%FvTvJ^@`+cF%cb~Oj|pHE=8;3hGd7z>}} zK=~EraA(2pJQB#Z1K33(+Tj;lfFioJQ&0Wwrn}%;^L(F#nN(~P{v5mrfshZlG=(Vw z(CqUGfSm_Z;oz#@LBTMF&AA4(qw|fbt5fbd3oFy+D--M&ykqcJPUQ@T8N&r> zs>%e8VO6FJZe&G9>D%~@iVec$?R$W^p5iHt9oM)MD+;ZwAQ{R7Tx$Qno~{z9%+olL z-xy}X&#Ratr^SlanIvHsk^91&$5qae&km22?LndRiS^xjA%?Fe(u=ZiL(ZZwD(H5H zOZ!&+`lwBDdC*zDcdD8d@Ugn}IoZ1{@xf9(%3!(-APbp@#4gGl>xy6&p$Z)Gd!&%) zakVizTlW|7!cpv!N8nhxuL8*ZaTAwrdY;F=8*@_xOtwrly%37BN9$WRMZ5rXxYx(B z`mPK8WzUgQhA&#C%|Yq+916dTA3Tw`OZ$|WYv=J$k7!P86`9Uwr#PpVa77x?iXs~I>qL|k%~*K2Ez2aVRT+uy*fN@8!NXnQ-emAj zi7NL2FM1nW(g$$eK()*p@tPpsLF^J?p7A*|Cvj9`IEeTXpNP;ss)_OW9$<4Zb5x(2 z!%Z#eH*np{@dA#|L4Rb2dQCUeD}dr>K+nj^*l~+?*hD2j=Q+{g&Dho=wsCyUnxKrp z#`a9HImRPH?nPu1&dtxSZEI$0g|PxKX-bIbO1kOkqLb@0V?xk>NH4^5p=TQG)0n#$ zq?cEzPlV~pcs#C~JOkLjgV&PY?buCiO!BJP%v!o+$&!s=G&sUOGJE)c>^0)XJA00d ztKsXXiDrUV>R~oK=FSftdeR`78(3Ixg{iBU=jxPwM)X_w`bZuVG@R`7W&AuU;tgr7 z(Zr>s{Lmsaka_+CzIC&H^JJVa-+|@|C|Z~@0NPA4a}i!nO54KrP?!oGDF#Wi${=6S zy?)xdd2~(9(OiuD?8_B}R2WS@pDfU7_64gunQ|cI(n#bp!?%;k&g0Iu`El>Bc*TJb zUDw1P0|;#MvDCy{npedrX&0qAnr01L1z8*kbfH@2TkK4|kd|=5nSbOpSXg1?J zNZc1G7JV+wSQx2ITf0B<}GPi%pbdzBm51E+E^}z}=V6 z{;8JQ*v=6@wE%NnGQ03(&!Kcj@+mHRWzVO^Sy5 z$YsT=b{2_vODB=8?2MOI@k~-admqz+xif0a8x1m{l4N$$apkoEH`>t=an)U)N;7&E z{>M5Z`2Xjm`~O)}@^l1E?$gL_{`fj;G}GdV=hz1I{*yKDj3=8kLB(jc)-68=^D4)i zWM`*`p4bQn9yN$ka7I&$mCZ#LiqZ|*6hU5%B)>4cbxR~@ECI(;3qGn?;Db30Fwt!l zKi^jmiG26yWcj_4Knm!;-LiOk*3+=8;ZA|4g_S+g;Sl$BDt42p-9AdzF#Gaz&S_1S zft|Ir^=X7^AjtMSYSY6A=?BCNzYf;11csP2BcTiJbm!V1(0qIq`v>;Vbn)Hqc0qMO+liq{`P!WEB5(vGj5Frq1KnNg3x)g&X zNJn~!6e-d-{=RR&-JRLLcV=h4H*@bh_q=mYyXT&J?z}`(V;x#5b}9e>K&z*#X$Alg ziT`O7NW1bE{x0BSR$@ ze_u%_SASkE}=<2Ryrm6kU>4cFopJ#CJQzalUEG$eiOh(c_&;xi! zQBe^nB@L98mLO0_1V#7-JB3U51@Zrb$$#?EbPI9`^m-cX42_H;Qe{?r zrh+F&Mn-y}&+*D^Sdll7oSd8hhv>t&`T)q%`>Q zV|IALL}`w^)Q@KUVJwuf%8$Mwt(u z`OaroE1xbP!MLvXLZ|sQ9nIl>|F^RHPMh;QkrL{|!1v^SWse~Het~c;B z5zVCVWe=Fqd9Lz}SV{s|HD^P(H$#omw+N+restW(oYLQNPw= z9gG}i?pc#JOMoIVs0rmq1Ddt2wQVAn&J`XW{LB&ZqCsIEKd5D!O6SysD!h8f(gJgA z?xbYJqlN@NyBkU4hv_6w&Pz2rHXDu8HbF@%kxa$Mb0}7xZe+RDjaUl?!bnb>2;nWt ztmz0N{e3f(mf8YmF8&HY+|A7ENQ)+)j0qrH)~!6EzpB1XoFEtMM@3pWqAecVu}QJ6 zD77r4S^~D?QLI#F%KSuMlX{Q1YKARO8yoC$92aIvRW8Xoe* zplk0AY)nQ3erYq5Pt7NBftGwpl_))f0+&Q+d~d44sU4nnl09P!HRX2<0VomJwezPK zWUay#xxR#>>mizDrp6ZUsbiLEb&l>=n^>ztupTp+47`pKg5xo5(IcV4qGTqz9ZGy2 zK5rb@el;=JfuNpi!rmqPe54(2`M9mG0|7F<9TT3ru<~g2k;SdfFu_?5e)7)x?C$ z?lb!q>dOFSVWeHT=TH;-qY35Rr-%M!s;3;IgUz@Yy%?(Kb{da1jTnkX_4^qAN6OK= ziu+SFIxVt}MuTmpu?ECyyho9j2PK&{EtZiQCawJNj19qWSsv zf14a%Y_yV#g_7Cm&Yl}|Y--2GwqYShi1&O+ia{TLaF%S-67 z@8#iIVbn#XHoty+&s*=x)xSzHty~=U=Eg!xrWHfCv(tcWYK1~z_%yZ<-5?wZx~tZouJIb+I4i^GK+ zEd2dM)YpEg12p*~31i=x+beA*C{Gk>G?JI4Wdiu41lPwGQ8N`PV71W^Mc=N+TvZlh z|2|>j_1r5tB}ywjFMZ+ON*QE9oAaE!@6`KF2;@!oCOiSwtzW83v1|c>z;L;xwiL^z z1nB=7XZIp-;KbTladu~LG_Ke~F(dDeyzxkBu`p!ur5YVEX(e|}Qrm}=6`C@a-o!z% z&`BtA!??l3uIIb_BL;3XVb_H8n6JNhujBUC%ajcW7@7}w1 z^z(9Zdz%Y|^~z5FRaV`#_WlAtk3KtICPwkp%Of`?yxiD&QD!3C^L%O@k<>aRSFa8NCj!YrOeAlfvD8tA zZ(M$#y@GdEgusgD`YM&Y8cB-nPY1K`vpX$c1sOiJPjKTfd-a8V14_Q>$Mq6w9fP44 zw4%o)_9}Lvee4feZNUqRvqfK@hd^4nD#=j=Js3U7=u&m>`FLB@e$?MTphE>JY^fgr0dw6GWI-zgeDF;6GZ>TH5rjbhE(pY^Zg-Fy7IcjEh)^tF` zPFo1q0xmX&YZn&_9(JikBx2x%a4~-y{(4}cd1UN``jT4rYoOBZE>dG^+>5MY`9ZCB z#oKA4UX-iP*20_}_{ZnHgq5)bOIKpGK2%avNsnCBi?4I6^>^OcsUl!#)0c>I>@Dg; zAonDC4KzCzw^uPS<<}R?lIo2Ie>{a%$tc(fFaoZ48Hspka#NN+O6ffZPNhOmY#~Xb z9&M7A6{h6a=$y2u@t2E@vQ}QB?zFv#%pqLYv2lv`M#m>(z`7>Hm}31iMEs*Wx`JA! z=hq_jjhdGZ*Ss@G5K9*OjCpn2#N?ywIQv$I)Vnx)SgYTZa-Q6vL0Fz%A1c_ZfMSf2 zsDO2hk~ktUpz0@U5b7q8nB_#XtJ8ZO=j3Fvh+*E}b-o!YHCLU=S13o8r>=B7o+* zhB&JUQv|;)K1?rkWsK&DRxM73ei0?83t|iR955)->n*PYezHwnS{LL{%;K6spN#}F zBPW8`tbc+UoMyGY6(4@yp7rmaoW}Bg06)AQHCF!wR$3{hpAM@NB*YCUIs`Ut<~29S z?P$x5$d;li+C=iY-bXj=VsU(BUs%=<# z?A3(J0PBytE1is_RN{_2V?3Hoeix(?iq8(si!mLrvH^ZVB|g>!A&1LaO@#SH9VZr8 zUVad2ffmEa5xuF1m{nMTOuC-Z#O;{TsW1s9VWRfMq(?aAEZGerFV_}7`P}(-UmMOT zu$aP-UPoZX#3SWWBx8G&0Yg4t-}$DiRn&gUG*GqAe60(9>~lM$fSj7y8UnFc-*jl% z1x2bziwNNOf_?BK8^C1P7yh~fp)H-mQrzZGuC0&4qN?{JoCd^rD)8Cu2R}II$mse_ z{4v~CVdXXE^kfuPT9t>_-TcT9&0}-*z83@c;K!-2v;3&vF6-`V%RDtsaSPFPZ*Efd zXx|7&M!-ow3$HbqfKQc9Hd4s@f{%lmVG-3I3A^?O_F`Hesmw{s+L9*^?mN&zF&>$^ zwFVniF_Yr!5ok9DVX8B9v#}4?iB^hjUJWwQ$o#wjGk$8uAmVoXi05pY*n90C2N}NB zR<9V!otkP{Fj}#E!DzOD^@?+Yu>c~mYp_XwdQ4$T89y3w4-1=(T4AVIvA&_jV4@%29%{td z)a8Iil*Jzx9{lJUwDS5ZWY)u#B>+J#F)Nv%a$ti#u`YtZs!z9@W%X+GLy3T@mgKlk zqG*T$E;mr<@UG%TCvPvJWjNiz`+3PuGsj^2cZE+bTUAPBr6gC|e4z%}S1YVy{a`Mj zbyve0AJMH!{N(8^w)WHMy*uFgqhQw;PAF;z^5c-;`GOA_2+TCPrf%d}m+jU1o@|Yz zHLJaNaz_ojyAwpGws;_XB4lE@bzQj0WS(zL?^Bz2{DO3*>=#FvnOF6^{z9@S6WgvT zJtveB;vnC&9VL{bo}XiM9i_{%T5CnT&A^OkcQe#Yt%k-PULlQBhB}MEcICgDh9WQp z+N|CH4j5313%4KA;c)FTgqSyn;?1+5b&XE;ZMGgk#{wcKPBmSF85*y?)^&R1t9rrD zfdjiZcU?Un8QYj_%!rccL$o+!iNIXHag>0+)0*8w63HcNJNi|>Tg0HB6$f}S%w7c+ zaAX@E*f)Xe?NyO^cofs9Y+o8%aa{DhHChI7m;`48DAB($r0Dyrm;H~VF6>YaKe81kjvx0zXa;Xn>dfP4A{ok6kn5i7JoWYO9v2E3iBrfe} z#aG*m(oVY(znV9>p3LX!X0H$ zn&+Ymc*hcn3#7&pNea{)ZN(C3BhvWXninO_IPEcxf+S|x{@01#)9~sr!}D+915qA$ z*R$UhWWr-ZHJQRIhB26TjMwtqh!>4=balh%Wo#wu=F}mC+nb2DzjqDh{?MpRXsYEo zu(@|{Q+ixT86p0YShBC0k*uodpr16(f4lw=T~_VM#WtE_m=!;?A4OUzj+7?cv&1q#B*0?qe=A#H6Twxuo}CjmMbtR4*e$=Me-V zmwSk@M5f9c>uv|8HuWKrb{7xxO;e}pLhTpbJT!j{ev(=zAt{mh18(xfic{XGsWZ1(W{Qm?oKsYD01*}9 z$Ao&(t-P^cjF^mt*O94}$n+U$odDtdG4oBcB&Cv6>kthmDgI62sz)e=OQ-m%DQ?;l zQspkW+8%vBl#mh(P+Io%A|DCGQZ-?!f?VTUvLbe(F4-e~PS3xZ@ z(SqHB{NidI?2Chsy|5!`Oesqr<6l$t-kz@6k!Q3SH+ET2JYc9_1G0v9iB(Z!IV#oW zQ~Ybb-$J#M3g~a(SF~>qbO+!X9CYpRJ3Rci#_q}|D@WNrY8fs+%3mlsVn+r%F-B0| z&8B%04y_yP-l{VC@)a3{EbysR>v4Zq7&*AITAwN_$iTU0jk@<{?j_1teDU2|Ju zIHcalrb=jW>wwr!B`?S5ZAQRbkM7CB43z9PD)c(q&^fIqIQkKtEAVckNz+`>royn6 ze7C_nUV@`vShIR^Tl0W`Njv2QG;xC;RfW=o*dWm1^~ubbY>j4b;C3>)ef2r!&iKkf zA!*BCwq>dyuV!t%j(t%6FS_dYU>J>zS54l>liDko?TqDjsh|CKZKlZO9Ps$Uoaz@~ z_>jQL*sC?r2zyXf$WayAZC(cIvIiHnTgF;w|te zc)ScO*YZl^I|eQw%psbqfN%0@v{vr!Q7Az}bCvaUqb&BN{8J*NXgHdDexEPtj32N# zJpz?1xIRq0pbSdsG zHvgo5+z&gpPj}qyYT@CyCytK$S{&Ee6+CzT%NyL^^fk)R2mIi{*XPT6VO|Q}2T+7Q%VhUH?4i%|!B}p$p z1h=boE*Gse-4_}mFnd0Np_1Ix5Xu@r6luk8uYTcf63N$F4l Date: Fri, 22 Mar 2019 09:03:58 +0100 Subject: [PATCH 0060/5147] Feat - Makes C0412 (ungrouped-imports) compatible with isort (#2824) Closes #2806. --- CONTRIBUTORS.txt | 3 +++ ChangeLog | 5 +++++ doc/whatsnew/2.4.rst | 12 ++++++++++++ pylint/checkers/imports.py | 9 +++++++-- pylint/test/functional/disable_ungrouped_imports.py | 2 +- pylint/test/functional/disable_wrong_import_order.py | 2 +- pylint/test/functional/ungrouped_imports.py | 8 +++++++- .../functional/ungrouped_imports_isort_compatible.py | 6 ++++++ .../ungrouped_imports_isort_compatible.txt | 0 9 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 pylint/test/functional/ungrouped_imports_isort_compatible.py create mode 100644 pylint/test/functional/ungrouped_imports_isort_compatible.txt diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index cb37e62553..464a6a951a 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -285,3 +285,6 @@ contributors: * Bluesheeptoken: contributor * Michael Scott Cuthbert: contributor + +* Pierre Sassoulas : contributor + - Made C0412 (ungrouped import) compatible with isort diff --git a/ChangeLog b/ChangeLog index 160dcaf1cf..afc7dfa1b6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -56,6 +56,11 @@ Release date: TBA Close #2816 +* C0412 (ungrouped-import) is now compatible with isort. + + Close #2806 + + What's New in Pylint 2.3.0? =========================== diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index b96e0992ff..fb9de74dff 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -66,3 +66,15 @@ Other Changes command line. In addition to the ``--from-stdin`` flag a (single) file name needs to be specified on the command line, which is needed for the report. + +* The checker for ungrouped imports is now more permissive. + +The import can now be sorted alphabetically by import style. +This makes pylint compatible with isort. + +The following imports do not trigger an ``ungrouped-imports`` anymore :: + + import unittest + import zipfile + from unittest import TestCase + from unittest.mock import MagicMock diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index 6fc5400bcc..0a2270ba74 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -504,14 +504,19 @@ 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 imports are grouped by package within a given category - met = set() + # 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) diff --git a/pylint/test/functional/disable_ungrouped_imports.py b/pylint/test/functional/disable_ungrouped_imports.py index cfcc01f82a..836c76d1a6 100644 --- a/pylint/test/functional/disable_ungrouped_imports.py +++ b/pylint/test/functional/disable_ungrouped_imports.py @@ -2,7 +2,7 @@ imports from being considered ungrouped in respect to it.""" # pylint: disable=unused-import,relative-import,wrong-import-position,wrong-import-order,using-constant-test # pylint: disable=import-error -import os +from os.path import basename import logging.config # pylint: disable=ungrouped-imports import os.path import logging diff --git a/pylint/test/functional/disable_wrong_import_order.py b/pylint/test/functional/disable_wrong_import_order.py index f725d482af..1fdf83fb10 100644 --- a/pylint/test/functional/disable_wrong_import_order.py +++ b/pylint/test/functional/disable_wrong_import_order.py @@ -8,4 +8,4 @@ import os.path import sys from astroid import are_exclusive -import first_party # [ungrouped-imports] +from first_party.bar import foo # [ungrouped-imports] diff --git a/pylint/test/functional/ungrouped_imports.py b/pylint/test/functional/ungrouped_imports.py index e631e60b18..2fcf18afcd 100644 --- a/pylint/test/functional/ungrouped_imports.py +++ b/pylint/test/functional/ungrouped_imports.py @@ -14,7 +14,13 @@ from os import pardir import scipy from os import sep -import astroid # [ungrouped-imports] +from astroid import exceptions # [ungrouped-imports] if True: import logging.handlers # [ungrouped-imports] from os.path import join # [ungrouped-imports] +# Test related to compatibility with isort: +# We check that we do not create error with the old way pylint was handling it +import subprocess +import unittest +from unittest import TestCase +from unittest.mock import MagicMock diff --git a/pylint/test/functional/ungrouped_imports_isort_compatible.py b/pylint/test/functional/ungrouped_imports_isort_compatible.py new file mode 100644 index 0000000000..2e64ed402a --- /dev/null +++ b/pylint/test/functional/ungrouped_imports_isort_compatible.py @@ -0,0 +1,6 @@ +"""Checks import order rule with imports that isort could generate""" +# pylint: disable=unused-import +import astroid +import isort +from astroid import are_exclusive, decorators +from astroid.modutils import get_module_part, is_standard_module diff --git a/pylint/test/functional/ungrouped_imports_isort_compatible.txt b/pylint/test/functional/ungrouped_imports_isort_compatible.txt new file mode 100644 index 0000000000..e69de29bb2 From 40b71e6f6dfdafd7417da43ce1d75f8899ed6a54 Mon Sep 17 00:00:00 2001 From: Nathan Marrow Date: Mon, 7 Jan 2019 16:15:27 -0500 Subject: [PATCH 0061/5147] Don't recurse in inherit_from_std_ex Commit 79c71de changed inherit_from_std_ex to pass recurs=True to the call to ancestors. Since the ancestors call now recurses, there is no need for the inherit_from_std_ex function to recurse as well, especially since ancestors handles circular references (A inherits from B which inherits from A). --- CONTRIBUTORS.txt | 2 ++ ChangeLog | 4 ++++ pylint/checkers/utils.py | 17 ++++++++++------- pylint/test/unittest_checkers_utils.py | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 464a6a951a..e0ba29e97a 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -288,3 +288,5 @@ contributors: * Pierre Sassoulas : contributor - Made C0412 (ungrouped import) compatible with isort + +* Nathan Marrow diff --git a/ChangeLog b/ChangeLog index afc7dfa1b6..f4276492d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Fix crash when calling ``inherit_from_std_ex`` on a class which is its own ancestor + + Close #2680 + * Added a new check that warns the user if a function call is used inside a test but parentheses are missing. Close #2658 diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 38dca2a386..4d7efe249e 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -671,14 +671,17 @@ def inherit_from_std_ex(node: astroid.node_classes.NodeNG) -> bool: Return true if the given class node is subclass of exceptions.Exception. """ - if ( - node.name in ("Exception", "BaseException") - and node.root().name == EXCEPTIONS_MODULE - ): - return True if not hasattr(node, "ancestors"): - return False - return any(inherit_from_std_ex(parent) for parent in node.ancestors(recurs=True)) + ancestors = [] + else: + ancestors = node.ancestors() + 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: diff --git a/pylint/test/unittest_checkers_utils.py b/pylint/test/unittest_checkers_utils.py index 7552d8c7ce..057859c2dc 100644 --- a/pylint/test/unittest_checkers_utils.py +++ b/pylint/test/unittest_checkers_utils.py @@ -170,3 +170,18 @@ def test_parse_format_method_string(): keys, num_args, pos_args = utils.parse_format_method_string(fmt) keyword_args = len(set(k for k, l in keys if not isinstance(k, int))) assert keyword_args + num_args + pos_args == count + + +def test_inherit_from_std_ex_recursive_definition(): + node = astroid.extract_node( + """ + import datetime + class First(datetime.datetime): + pass + class Second(datetime.datetime): #@ + pass + datetime.datetime = First + datetime.datetime = Second + """ + ) + assert not utils.inherit_from_std_ex(node) From ac5b1a434a8dddf3cfea361eb5c65669e76dc9b1 Mon Sep 17 00:00:00 2001 From: Nathan Marrow Date: Tue, 8 Jan 2019 12:32:39 -0500 Subject: [PATCH 0062/5147] Collapse if/else into ternary --- pylint/checkers/utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 4d7efe249e..352608f37f 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -671,10 +671,7 @@ def inherit_from_std_ex(node: astroid.node_classes.NodeNG) -> bool: Return true if the given class node is subclass of exceptions.Exception. """ - if not hasattr(node, "ancestors"): - ancestors = [] - else: - ancestors = node.ancestors() + ancestors = node.ancestors() if hasattr(node, "ancestors") else [] for ancestor in itertools.chain([node], ancestors): if ( ancestor.name in ("Exception", "BaseException") From 75a047916521c5d3d23a7a63b8def59677061633 Mon Sep 17 00:00:00 2001 From: Dan Hemberger <846186+hemberger@users.noreply.github.com> Date: Sun, 24 Mar 2019 01:43:25 -0700 Subject: [PATCH 0063/5147] Fix last updated date in sphinx documentation (#2828) The "last updated" date was displaying as "None", which we can fix by uncommenting `html_last_updated_fmt` in the sphinx configuration. --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index f71c4d3415..b214a14ccb 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -138,7 +138,7 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' +html_last_updated_fmt = '%b %d, %Y' smartquotes = False From 24687f5dfbaf03bb35d9b5d788b67ce7b5d416d0 Mon Sep 17 00:00:00 2001 From: "Taewon D. Kim" Date: Sat, 23 Mar 2019 08:30:02 -0400 Subject: [PATCH 0064/5147] Ignore raw docstrings in Similarities check with ignore-docstring=yes Raw docstrings is a type of docstring (PEP 257), so they too should be ignored - Add raw docstring to be removed with `ignore_docstring=True` inside `pylint.checkers.similar.stripped_lines` --- pylint/checkers/similar.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py index f65f533cd9..1ac5f6196b 100644 --- a/pylint/checkers/similar.py +++ b/pylint/checkers/similar.py @@ -176,7 +176,9 @@ def stripped_lines(lines, ignore_comments, ignore_docstrings, ignore_imports): for lineno, line in enumerate(lines, start=1): line = line.strip() if ignore_docstrings: - if not docstring and (line.startswith('"""') or line.startswith("'''")): + if not docstring and any( + line.startswith(i) for i in ['"""', "'''", 'r"""', "r'''"] + ): docstring = line[:3] line = line[3:] if docstring: From b14d5e950f69571a3109e7cfb2d91614dadb66f3 Mon Sep 17 00:00:00 2001 From: "Taewon D. Kim" Date: Sat, 23 Mar 2019 08:32:57 -0400 Subject: [PATCH 0065/5147] Add/modify tests for checking raw docstring in Similarities checker - Add an extra case to the examples used to test similarities checker - Modify outputs of tests (total number of lines and the percent changed) --- pylint/test/input/similar1 | 8 ++++++++ pylint/test/input/similar2 | 8 ++++++++ pylint/test/unittest_checker_similar.py | 8 ++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/pylint/test/input/similar1 b/pylint/test/input/similar1 index 2b04ee2f5f..a533e2f6f1 100644 --- a/pylint/test/input/similar1 +++ b/pylint/test/input/similar1 @@ -20,3 +20,11 @@ fifteen sixteen seventeen eighteen + + + + +nineteen +r""" +twenty +""" diff --git a/pylint/test/input/similar2 b/pylint/test/input/similar2 index 77f5f1ed6b..79bcab2fd9 100644 --- a/pylint/test/input/similar2 +++ b/pylint/test/input/similar2 @@ -20,3 +20,11 @@ FIFTEEN sixteen seventeen eighteen + + + + +NINETEEN +r""" +twenty +""" diff --git a/pylint/test/unittest_checker_similar.py b/pylint/test/unittest_checker_similar.py index a84883372d..7470e06c9c 100644 --- a/pylint/test/unittest_checker_similar.py +++ b/pylint/test/unittest_checker_similar.py @@ -48,7 +48,7 @@ def test_ignore_comments(): eight nine ''' ten -TOTAL lines=44 duplicates=10 percent=22.73 +TOTAL lines=60 duplicates=10 percent=16.67 """ % (SIMILAR1, SIMILAR2) ).strip() @@ -84,7 +84,7 @@ def test_ignore_docsrings(): three four five -TOTAL lines=44 duplicates=13 percent=29.55 +TOTAL lines=60 duplicates=13 percent=21.67 """ % ((SIMILAR1, SIMILAR2) * 2) ).strip() @@ -99,7 +99,7 @@ def test_ignore_imports(): assert ( output.getvalue().strip() == """ -TOTAL lines=44 duplicates=0 percent=0.00 +TOTAL lines=60 duplicates=0 percent=0.00 """.strip() ) @@ -169,7 +169,7 @@ def test_ignore_nothing(): three four five -TOTAL lines=44 duplicates=5 percent=11.36 +TOTAL lines=60 duplicates=5 percent=8.33 """ % (SIMILAR1, SIMILAR2) ).strip() From 8acc80f7f3bcf055ad7a0ac06f8f5b6faf8a12c2 Mon Sep 17 00:00:00 2001 From: "Taewon D. Kim" Date: Sat, 23 Mar 2019 08:34:56 -0400 Subject: [PATCH 0066/5147] Add ChangeLog entry for ignoring raw docstring in Similarities checker --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index f4276492d9..01941440c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,8 @@ What's New in Pylint 2.4.0? Release date: TBA +* Ignore raw docstrings when running Similarities checker with `ignore-docstrings=yes` option + * Fix crash when calling ``inherit_from_std_ex`` on a class which is its own ancestor Close #2680 From 6989448dc461daafc2605623546d8d0f5fe4633f Mon Sep 17 00:00:00 2001 From: "Taewon D. Kim" Date: Sat, 23 Mar 2019 08:35:45 -0400 Subject: [PATCH 0067/5147] Add Contributor for ignoring raw docstring in Similarities checker --- CONTRIBUTORS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index e0ba29e97a..84019234e0 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -290,3 +290,5 @@ contributors: - Made C0412 (ungrouped import) compatible with isort * Nathan Marrow + +* Taewon Kim : contributor From 3228bc4184c43c2d96f9360b4adfc39c151a00df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Renvois=C3=A9?= Date: Fri, 15 Mar 2019 22:17:21 +0100 Subject: [PATCH 0068/5147] Add preferred-modules option and check This allow users to specify a set of preferred modules that should be used instead of other modules. --- CONTRIBUTORS.txt | 1 + ChangeLog | 5 +++ doc/whatsnew/2.4.rst | 9 +++++ pylint/checkers/imports.py | 37 +++++++++++++++++++++ pylint/test/functional/preferred_module.py | 5 +++ pylint/test/functional/preferred_module.rc | 2 ++ pylint/test/functional/preferred_module.txt | 2 ++ 7 files changed, 61 insertions(+) create mode 100644 pylint/test/functional/preferred_module.py create mode 100644 pylint/test/functional/preferred_module.rc create mode 100644 pylint/test/functional/preferred_module.txt diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 84019234e0..cf08440f5f 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -292,3 +292,4 @@ contributors: * Nathan Marrow * Taewon Kim : contributor + diff --git a/ChangeLog b/ChangeLog index 01941440c6..78e1c0e8f8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,6 +25,11 @@ Release date: TBA Close #2684 +* Add ``preferred-module`` checker that notify if an import has a replacement module that should be used. + + This check is emitted when ``pylint`` finds an imported module that has a + preferred replacement listed in ``preferred-modules``. + * ``assigning-non-slot`` not emitted for classes with unknown base classes. Close #2807 diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index fb9de74dff..30fe87af04 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -33,6 +33,15 @@ New checkers __slots__ = ('first', 'second') first = 1 +* A new check ``preferred-module`` was added. + + This check is emitted when ``pylint`` finds an imported module that has a + preferred replacement listed in ``preferred-modules``. + + For example, you can set the preferred modules as ``xml:defusedxml,json:ujson`` + to make ``pylint`` suggest using ``defusedxml`` instead of ``xml`` + and ``ujson`` rather than ``json``. + Other Changes ============= diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index 0a2270ba74..bca30c7b95 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -23,6 +23,7 @@ # Copyright (c) 2018 Mike Frysinger # Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com> # Copyright (c) 2018 Marianna Polatoglou +# Copyright (c) 2019 Paul Renvoise # 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 @@ -237,6 +238,11 @@ def _make_graph(filename, dep_info, sect, gtype): "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", @@ -275,6 +281,7 @@ def _make_graph(filename, dep_info, sect, gtype): DEFAULT_STANDARD_LIBRARY = () DEFAULT_KNOWN_THIRD_PARTY = ("enchant",) +DEFAULT_PREFERRED_MODULES = () class ImportsChecker(BaseChecker): @@ -283,6 +290,7 @@ class ImportsChecker(BaseChecker): * relative / wildcard imports * cyclic imports * uses of deprecated modules + * uses of modules instead of preferred modules """ __implements__ = IAstroidChecker @@ -295,6 +303,7 @@ class ImportsChecker(BaseChecker): deprecated_modules = ("optparse",) else: deprecated_modules = ("optparse", "tkinter.tix") + options = ( ( "deprecated-modules", @@ -306,6 +315,16 @@ class ImportsChecker(BaseChecker): " separated by a comma.", }, ), + ( + "preferred-modules", + { + "default": DEFAULT_PREFERRED_MODULES, + "type": "csv", + "metavar": "", + "help": "Couples of modules and preferred modules," + " separated by a comma.", + }, + ), ( "import-graph", { @@ -393,6 +412,13 @@ def __init__(self, linter=None): ("RP0402", "Modules dependencies graph", self._report_dependencies_graph), ) + # Build a mapping {'module': 'preferred-module'} + self.preferred_modules = dict( + module.split(":") + for module in self.config.preferred_modules + if ":" in module + ) + self._site_packages = self._compute_site_packages() @staticmethod @@ -457,6 +483,7 @@ def visit_import(self, 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 @@ -479,6 +506,7 @@ def visit_importfrom(self, node): 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) @@ -826,6 +854,15 @@ def _check_deprecated_module(self, node, mod_path): 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: diff --git a/pylint/test/functional/preferred_module.py b/pylint/test/functional/preferred_module.py new file mode 100644 index 0000000000..ce1f1c2c8b --- /dev/null +++ b/pylint/test/functional/preferred_module.py @@ -0,0 +1,5 @@ +"""Test preferred modules.""" +# pylint: disable=unused-import + +import json # [preferred-module] +from re import search # [preferred-module] diff --git a/pylint/test/functional/preferred_module.rc b/pylint/test/functional/preferred_module.rc new file mode 100644 index 0000000000..1e9b0f66a4 --- /dev/null +++ b/pylint/test/functional/preferred_module.rc @@ -0,0 +1,2 @@ +[IMPORTS] +preferred-modules="json:ujson,re:regex" diff --git a/pylint/test/functional/preferred_module.txt b/pylint/test/functional/preferred_module.txt new file mode 100644 index 0000000000..7d327cac42 --- /dev/null +++ b/pylint/test/functional/preferred_module.txt @@ -0,0 +1,2 @@ +preferred-module:4::Prefer importing 'ujson' instead of 'json' +preferred-module:5::Prefer importing 'regex' instead of 're' From b20a2984c94e2946669d727dbda78735882bf50a Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 27 Mar 2019 15:01:16 +0100 Subject: [PATCH 0069/5147] Move the preferred construction in open() where it has access to config --- pylint/checkers/imports.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index bca30c7b95..1405629fea 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -412,13 +412,6 @@ def __init__(self, linter=None): ("RP0402", "Modules dependencies graph", self._report_dependencies_graph), ) - # Build a mapping {'module': 'preferred-module'} - self.preferred_modules = dict( - module.split(":") - for module in self.config.preferred_modules - if ":" in module - ) - self._site_packages = self._compute_site_packages() @staticmethod @@ -455,6 +448,12 @@ def open(self): 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 + ) def _import_graph_without_ignored_edges(self): filtered_graph = copy.deepcopy(self.import_graph) From 8a0574ead7c69c4ddeb2e2f478fe973896ad1121 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 27 Mar 2019 15:02:50 +0100 Subject: [PATCH 0070/5147] Drop the quotes to make the test actually work --- pylint/test/functional/preferred_module.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylint/test/functional/preferred_module.rc b/pylint/test/functional/preferred_module.rc index 1e9b0f66a4..e0982e0304 100644 --- a/pylint/test/functional/preferred_module.rc +++ b/pylint/test/functional/preferred_module.rc @@ -1,2 +1,2 @@ [IMPORTS] -preferred-modules="json:ujson,re:regex" +preferred-modules=json:ujson,re:regex From 95232ca07a5514f1242c7fb11a7504a24e78192d Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 28 Mar 2019 09:33:20 +0100 Subject: [PATCH 0071/5147] ``epylint.py_run`` defaults to ``python`` in case the current executable is not a Python one. Close #2837 --- ChangeLog | 4 ++++ pylint/epylint.py | 5 ++++- pylint/test/test_regr.py | 1 - 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 78e1c0e8f8..79539ed2bb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* ``epylint.py_run`` defaults to ``python`` in case the current executable is not a Python one. + + Close #2837 + * Ignore raw docstrings when running Similarities checker with `ignore-docstrings=yes` option * Fix crash when calling ``inherit_from_std_ex`` on a class which is its own ancestor diff --git a/pylint/epylint.py b/pylint/epylint.py index c2d4a21d8c..8e246e55c8 100755 --- a/pylint/epylint.py +++ b/pylint/epylint.py @@ -149,8 +149,11 @@ def py_run(command_options="", return_std=False, stdout=None, stderr=None): To silently run Pylint on a module, and get its standard output and error: >>> (pylint_stdout, pylint_stderr) = py_run( 'module_name.py', True) """ + # Detect if we use Python as executable or not, else default to `python` + executable = sys.executable if "python" in sys.executable else "python" + # Create command line to call pylint - epylint_part = [sys.executable, "-c", "from pylint import epylint;epylint.Run()"] + epylint_part = [executable, "-c", "from pylint import epylint;epylint.Run()"] options = shlex.split(command_options, posix=not sys.platform.startswith("win")) cli = epylint_part + options diff --git a/pylint/test/test_regr.py b/pylint/test/test_regr.py index 65e66eb20e..e91d4464b4 100644 --- a/pylint/test/test_regr.py +++ b/pylint/test/test_regr.py @@ -22,7 +22,6 @@ import pytest import pylint.testutils as testutils -from pylint import epylint REGR_DATA = join(dirname(abspath(__file__)), "regrtest_data") sys.path.insert(1, REGR_DATA) From af7eda4a62a572f055febdee2528eca6e2037ca0 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Fri, 29 Mar 2019 09:19:04 +0100 Subject: [PATCH 0072/5147] Support postponed evaluation of annotations for variable annotations. Close #2838 --- ChangeLog | 4 ++++ pylint/checkers/variables.py | 4 +++- pylint/test/functional/postponed_evaluation_activated.py | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 79539ed2bb..57254affa9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Support postponed evaluation of annotations for variable annotations. + + Close #2838 + * ``epylint.py_run`` defaults to ``python`` in case the current executable is not a Python one. Close #2837 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 02974a19a1..8590413167 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1519,7 +1519,9 @@ def visit_name(self, node): # Handle postponed evaluation of annotations if not ( self._postponed_evaluation_enabled - and isinstance(stmt, astroid.FunctionDef) + and isinstance( + stmt, (astroid.AnnAssign, astroid.FunctionDef) + ) ): self.add_message( "used-before-assignment", args=name, node=node diff --git a/pylint/test/functional/postponed_evaluation_activated.py b/pylint/test/functional/postponed_evaluation_activated.py index 1a6cd5979e..6a150d84c8 100644 --- a/pylint/test/functional/postponed_evaluation_activated.py +++ b/pylint/test/functional/postponed_evaluation_activated.py @@ -2,6 +2,7 @@ # pylint: disable=too-few-public-methods,no-name-in-module from __future__ import annotations + class Class: @classmethod def from_string(cls, source) -> Class: @@ -13,3 +14,11 @@ def validate_b(self, obj: OtherClass) -> bool: class OtherClass: ... + + +class Example: + obj: Other + + +class Other: + ... From a2801fd820a7adc3c67afd21767ae2b022296cb5 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Fri, 29 Mar 2019 09:33:36 +0100 Subject: [PATCH 0073/5147] Pin sphinx to the latest and fix the warning it caused --- doc/user_guide/ide-integration.rst | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/user_guide/ide-integration.rst b/doc/user_guide/ide-integration.rst index 82a26f939d..71af09697e 100644 --- a/doc/user_guide/ide-integration.rst +++ b/doc/user_guide/ide-integration.rst @@ -225,7 +225,7 @@ Command-line arguments and configuration files See `Pylint command line arguments`_ for general switches. Command line arguments can be used to load Pylint plugins, such as that for Django: -.. code:: json +:: "python.linting.pylintArgs": ["--load-plugins", "pylint_django"] diff --git a/tox.ini b/tox.ini index b539381d2e..26f1e2fb37 100644 --- a/tox.ini +++ b/tox.ini @@ -93,7 +93,7 @@ usedevelop = True changedir = doc/ whitelist_externals = rm deps = - sphinx + sphinx==2.0 python-docs-theme commands = From 1dbbb35d93c19104b477c200afd3be756132a255 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Mar 2019 22:10:12 +0100 Subject: [PATCH 0074/5147] Refactor - Avoid intra-packages circular dependencies for constants Some constants were package internal but were used by multiple packages. This created circular dependencies. By creating a file for constants we make sure this does not happen because we won't import everything important in this file and every thing else can depend on it. --- pylint/checkers/format.py | 2 +- pylint/checkers/misc.py | 2 +- pylint/checkers/python3.py | 2 +- pylint/{message => }/constants.py | 14 ++++++++++++++ pylint/lint.py | 5 +++-- pylint/message/__init__.py | 9 --------- pylint/message/build_message_definition.py | 2 +- pylint/message/message.py | 2 +- pylint/message/message_definition.py | 2 +- pylint/message/message_handler_mix_in.py | 10 +++++----- pylint/test/unittest_lint.py | 9 ++++----- pylint/utils/__init__.py | 2 -- pylint/utils/constants.py | 13 ------------- pylint/utils/file_state.py | 3 +-- pylint/utils/utils.py | 3 +-- pylint/utils/warning_scope.py | 9 --------- 16 files changed, 34 insertions(+), 55 deletions(-) rename pylint/{message => }/constants.py (63%) delete mode 100644 pylint/utils/constants.py delete mode 100644 pylint/utils/warning_scope.py diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index 84b6b273ea..37963e407c 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -52,8 +52,8 @@ 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 -from pylint.utils import OPTION_RGX, WarningScope _ASYNC_TOKEN = "async" _CONTINUATION_BLOCK_OPENERS = [ diff --git a/pylint/checkers/misc.py b/pylint/checkers/misc.py index d12c2639e5..882d8ab2fc 100644 --- a/pylint/checkers/misc.py +++ b/pylint/checkers/misc.py @@ -24,9 +24,9 @@ import tokenize from pylint.checkers import BaseChecker +from pylint.constants import OPTION_RGX from pylint.interfaces import IRawChecker, ITokenChecker from pylint.message import MessagesHandlerMixIn -from pylint.utils import OPTION_RGX class ByIdManagedMessagesChecker(BaseChecker): diff --git a/pylint/checkers/python3.py b/pylint/checkers/python3.py index 43025d0c8b..5ee6cdceb4 100644 --- a/pylint/checkers/python3.py +++ b/pylint/checkers/python3.py @@ -38,8 +38,8 @@ 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 -from pylint.utils import WarningScope _ZERO = re.compile("^0+$") diff --git a/pylint/message/constants.py b/pylint/constants.py similarity index 63% rename from pylint/message/constants.py rename to pylint/constants.py index 37fd973386..07d1727ab2 100644 --- a/pylint/message/constants.py +++ b/pylint/constants.py @@ -3,6 +3,15 @@ # 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 +import re + +# Allow stopping after the first semicolon/hash encountered, +# so that an option can be continued with the reasons +# why it is active or disabled. +OPTION_RGX = re.compile(r"\s*#.*\bpylint:\s*([^;#]+)[;#]{0,1}") + +PY_EXTS = (".py", ".pyc", ".pyo", ".pyw", ".so", ".dll") + MSG_STATE_CONFIDENCE = 2 _MSG_ORDER = "EWRCIF" MSG_STATE_SCOPE_CONFIG = 0 @@ -22,3 +31,8 @@ MSG_TYPES_LONG = {v: k for k, v in MSG_TYPES.items()} MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1} + + +class WarningScope: + LINE = "line-based-msg" + NODE = "node-based-msg" diff --git a/pylint/lint.py b/pylint/lint.py index 25129ecb1d..48c97246e3 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -76,9 +76,10 @@ from pylint import checkers, config, exceptions, interfaces, reporters from pylint.__pkginfo__ import version -from pylint.message import MSG_TYPES, Message, MessagesHandlerMixIn, MessagesStore +from pylint.constants import MSG_TYPES, OPTION_RGX +from pylint.message import Message, MessagesHandlerMixIn, MessagesStore from pylint.reporters.ureports import nodes as report_nodes -from pylint.utils import OPTION_RGX, ASTWalker, FileState, ReportsHandlerMixIn, utils +from pylint.utils import ASTWalker, FileState, ReportsHandlerMixIn, utils try: import multiprocessing diff --git a/pylint/message/__init__.py b/pylint/message/__init__.py index 4b71f57b9d..6b97d52819 100644 --- a/pylint/message/__init__.py +++ b/pylint/message/__init__.py @@ -40,15 +40,6 @@ """All the classes related to Message handling.""" from pylint.message.build_message_definition import build_message_def -from pylint.message.constants import ( - _SCOPE_EXEMPT, - MSG_STATE_CONFIDENCE, - MSG_STATE_SCOPE_CONFIG, - MSG_STATE_SCOPE_MODULE, - MSG_TYPES, - MSG_TYPES_LONG, - MSG_TYPES_STATUS, -) from pylint.message.message import Message from pylint.message.message_definition import MessageDefinition from pylint.message.message_handler_mix_in import MessagesHandlerMixIn diff --git a/pylint/message/build_message_definition.py b/pylint/message/build_message_definition.py index 50c1152c3c..fd3654abba 100644 --- a/pylint/message/build_message_definition.py +++ b/pylint/message/build_message_definition.py @@ -5,9 +5,9 @@ import warnings +from pylint.constants import WarningScope from pylint.interfaces import IRawChecker, ITokenChecker, implements from pylint.message.message_definition import MessageDefinition -from pylint.utils.warning_scope import WarningScope def build_message_def(checker, msgid, msg_tuple): diff --git a/pylint/message/message.py b/pylint/message/message.py index f4a5376007..e2b0320e43 100644 --- a/pylint/message/message.py +++ b/pylint/message/message.py @@ -6,7 +6,7 @@ import collections -from pylint.message.constants import MSG_TYPES +from pylint.constants import MSG_TYPES _MsgBase = collections.namedtuple( "_MsgBase", diff --git a/pylint/message/message_definition.py b/pylint/message/message_definition.py index 257f7ce3a0..382df6c6d0 100644 --- a/pylint/message/message_definition.py +++ b/pylint/message/message_definition.py @@ -5,8 +5,8 @@ import sys +from pylint.constants import MSG_TYPES from pylint.exceptions import InvalidMessageError -from pylint.message.constants import MSG_TYPES from pylint.utils.normalize_text import normalize_text diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 7336979c25..240c62e572 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -8,10 +8,7 @@ import sys from inspect import cleandoc -from pylint.exceptions import InvalidMessageError, UnknownMessageError -from pylint.interfaces import UNDEFINED -from pylint.message.build_message_definition import build_message_def -from pylint.message.constants import ( +from pylint.constants import ( _MSG_ORDER, _SCOPE_EXEMPT, MSG_STATE_CONFIDENCE, @@ -19,7 +16,11 @@ MSG_STATE_SCOPE_MODULE, MSG_TYPES, MSG_TYPES_STATUS, + WarningScope, ) +from pylint.exceptions import InvalidMessageError, UnknownMessageError +from pylint.interfaces import UNDEFINED +from pylint.message.build_message_definition import build_message_def from pylint.message.message import Message from pylint.utils.utils import ( _format_option_value, @@ -27,7 +28,6 @@ get_module_and_frameid, normalize_text, ) -from pylint.utils.warning_scope import WarningScope def _rest_format_section(stream, section, options, doc=None): diff --git a/pylint/test/unittest_lint.py b/pylint/test/unittest_lint.py index 3e58558344..1674fdf803 100644 --- a/pylint/test/unittest_lint.py +++ b/pylint/test/unittest_lint.py @@ -42,15 +42,14 @@ import pylint.testutils as testutils from pylint import checkers, config, exceptions, interfaces, lint from pylint.checkers.utils import check_messages -from pylint.exceptions import InvalidMessageError, UnknownMessageError -from pylint.lint import ArgumentPreprocessingError, PyLinter, Run, preprocess_options -from pylint.message import ( +from pylint.constants import ( MSG_STATE_CONFIDENCE, MSG_STATE_SCOPE_CONFIG, MSG_STATE_SCOPE_MODULE, - MessageDefinition, - MessagesStore, ) +from pylint.exceptions import InvalidMessageError, UnknownMessageError +from pylint.lint import ArgumentPreprocessingError, PyLinter, Run, preprocess_options +from pylint.message import MessageDefinition, MessagesStore from pylint.reporters import text from pylint.utils import FileState, tokenize_module diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index 51b12f75dd..b4764a3b38 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -42,7 +42,6 @@ """ from pylint.utils.ast_walker import ASTWalker -from pylint.utils.constants import OPTION_RGX, PY_EXTS from pylint.utils.file_state import FileState from pylint.utils.normalize_text import normalize_text from pylint.utils.reports_handler_mix_in import ReportsHandlerMixIn @@ -63,4 +62,3 @@ safe_decode, tokenize_module, ) -from pylint.utils.warning_scope import WarningScope diff --git a/pylint/utils/constants.py b/pylint/utils/constants.py deleted file mode 100644 index 46a7e1be4f..0000000000 --- a/pylint/utils/constants.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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 - -import re - -# Allow stopping after the first semicolon/hash encountered, -# so that an option can be continued with the reasons -# why it is active or disabled. -OPTION_RGX = re.compile(r"\s*#.*\bpylint:\s*([^;#]+)[;#]{0,1}") - -PY_EXTS = (".py", ".pyc", ".pyo", ".pyw", ".so", ".dll") diff --git a/pylint/utils/file_state.py b/pylint/utils/file_state.py index 2bc1662aa7..468445cf38 100644 --- a/pylint/utils/file_state.py +++ b/pylint/utils/file_state.py @@ -7,8 +7,7 @@ from astroid import nodes -from pylint.message.constants import MSG_STATE_SCOPE_MODULE -from pylint.utils.warning_scope import WarningScope +from pylint.constants import MSG_STATE_SCOPE_MODULE, WarningScope class FileState: diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index 3147ac847d..d265e2c20b 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -14,8 +14,7 @@ from astroid import Module, modutils -from pylint.message.constants import MSG_TYPES, MSG_TYPES_LONG -from pylint.utils.constants import PY_EXTS +from pylint.constants import MSG_TYPES, MSG_TYPES_LONG, PY_EXTS from pylint.utils.normalize_text import normalize_text diff --git a/pylint/utils/warning_scope.py b/pylint/utils/warning_scope.py deleted file mode 100644 index bdad94d3fb..0000000000 --- a/pylint/utils/warning_scope.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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 - - -class WarningScope: - LINE = "line-based-msg" - NODE = "node-based-msg" From d84578f84ef8e4a7c73b7126e9321b2c4a4c7e37 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 18:47:16 +0100 Subject: [PATCH 0075/5147] Refactor - Remove circular import between utils and reporter ReportsHandlerMixIn was importing Nodes from reporter and is probably more suited for the reporter package anyway. --- pylint/lint.py | 3 ++- pylint/{utils => reporters}/reports_handler_mix_in.py | 0 pylint/utils/__init__.py | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) rename pylint/{utils => reporters}/reports_handler_mix_in.py (100%) diff --git a/pylint/lint.py b/pylint/lint.py index 48c97246e3..17a6f2f780 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -78,8 +78,9 @@ from pylint.__pkginfo__ import version from pylint.constants import MSG_TYPES, OPTION_RGX from pylint.message import Message, MessagesHandlerMixIn, MessagesStore +from pylint.reporters.reports_handler_mix_in import ReportsHandlerMixIn from pylint.reporters.ureports import nodes as report_nodes -from pylint.utils import ASTWalker, FileState, ReportsHandlerMixIn, utils +from pylint.utils import ASTWalker, FileState, utils try: import multiprocessing diff --git a/pylint/utils/reports_handler_mix_in.py b/pylint/reporters/reports_handler_mix_in.py similarity index 100% rename from pylint/utils/reports_handler_mix_in.py rename to pylint/reporters/reports_handler_mix_in.py diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index b4764a3b38..029bfd97c9 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -44,7 +44,6 @@ from pylint.utils.ast_walker import ASTWalker from pylint.utils.file_state import FileState from pylint.utils.normalize_text import normalize_text -from pylint.utils.reports_handler_mix_in import ReportsHandlerMixIn from pylint.utils.utils import ( _basename_in_blacklist_re, _check_csv, From d8116eac7c03d6889ba3b5eef8cbf53a8ba5b44b Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 18:59:44 +0100 Subject: [PATCH 0076/5147] Refactor - Merge normalize text in utils.py Now that there is no more circular import we can do that. --- pylint/message/message_definition.py | 2 +- pylint/utils/__init__.py | 2 +- pylint/utils/normalize_text.py | 15 --------------- pylint/utils/utils.py | 11 ++++++++++- 4 files changed, 12 insertions(+), 18 deletions(-) delete mode 100644 pylint/utils/normalize_text.py diff --git a/pylint/message/message_definition.py b/pylint/message/message_definition.py index 382df6c6d0..102ba387ba 100644 --- a/pylint/message/message_definition.py +++ b/pylint/message/message_definition.py @@ -7,7 +7,7 @@ from pylint.constants import MSG_TYPES from pylint.exceptions import InvalidMessageError -from pylint.utils.normalize_text import normalize_text +from pylint.utils import normalize_text class MessageDefinition: diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index 029bfd97c9..67b56db3a8 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -43,7 +43,6 @@ from pylint.utils.ast_walker import ASTWalker from pylint.utils.file_state import FileState -from pylint.utils.normalize_text import normalize_text from pylint.utils.utils import ( _basename_in_blacklist_re, _check_csv, @@ -57,6 +56,7 @@ format_section, get_global_option, get_module_and_frameid, + normalize_text, register_plugins, safe_decode, tokenize_module, diff --git a/pylint/utils/normalize_text.py b/pylint/utils/normalize_text.py deleted file mode 100644 index 787f03d00e..0000000000 --- a/pylint/utils/normalize_text.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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 - -import textwrap - - -def normalize_text(text, line_len=80, indent=""): - """Wrap the text on the given line length.""" - return "\n".join( - textwrap.wrap( - text, width=line_len, initial_indent=indent, subsequent_indent=indent - ) - ) diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index d265e2c20b..e78cd98a43 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -8,6 +8,7 @@ import codecs import re import sys +import textwrap import tokenize from os import linesep, listdir from os.path import basename, dirname, exists, isdir, join, normpath, splitext @@ -15,7 +16,15 @@ from astroid import Module, modutils from pylint.constants import MSG_TYPES, MSG_TYPES_LONG, PY_EXTS -from pylint.utils.normalize_text import normalize_text + + +def normalize_text(text, line_len=80, indent=""): + """Wrap the text on the given line length.""" + return "\n".join( + textwrap.wrap( + text, width=line_len, initial_indent=indent, subsequent_indent=indent + ) + ) def get_module_and_frameid(node): From 273492326c97871f289a68873ecfaad8a4168a6b Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 21:16:35 +0100 Subject: [PATCH 0077/5147] Refactor - Move refactor.utils function to the utils package This permit to have less cross dependency as the utils package does not depend on anything. The checker package still depends on reporter. Also moved classes from __init__ to their own file in reporter. --- pylint/checkers/__init__.py | 6 +- pylint/checkers/base.py | 7 +- pylint/checkers/raw_metrics.py | 2 +- pylint/lint.py | 5 +- pylint/reporters/__init__.py | 102 ++---------------- pylint/reporters/base_reporter.py | 68 ++++++++++++ pylint/reporters/collecting_reporter.py | 21 ++++ .../reporters/{json.py => json_reporter.py} | 2 +- pylint/test/test_self.py | 3 +- pylint/test/unittest_reporters_json.py | 2 +- pylint/utils/__init__.py | 1 + pylint/utils/utils.py | 18 ++++ 12 files changed, 125 insertions(+), 112 deletions(-) create mode 100644 pylint/reporters/base_reporter.py create mode 100644 pylint/reporters/collecting_reporter.py rename pylint/reporters/{json.py => json_reporter.py} (97%) diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py index f745037bc1..9f64bd68ba 100644 --- a/pylint/checkers/__init__.py +++ b/pylint/checkers/__init__.py @@ -39,15 +39,11 @@ """ -import sys -import tokenize -import warnings from typing import Any from pylint.config import OptionsProviderMixIn from pylint.interfaces import UNDEFINED -from pylint.reporters import diff_string -from pylint.utils import register_plugins +from pylint.utils import diff_string, register_plugins def table_lines_from_stats(stats, old_stats, columns): diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index bce4ae7fd3..4e5c1a0bbb 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -49,9 +49,8 @@ import astroid.scoped_nodes import pylint.utils as lint_utils -from pylint import checkers, exceptions, interfaces, reporters +from pylint import checkers, exceptions, interfaces from pylint.checkers import utils -from pylint.checkers.utils import get_node_last_lineno from pylint.reporters.ureports import nodes as reporter_nodes @@ -381,7 +380,7 @@ def report_by_type_stats(sect, stats, old_stats): new = stats[node_type] old = old_stats.get(node_type, None) if old is not None: - diff_str = reporters.diff_string(old, new) + diff_str = lint_utils.diff_string(old, new) else: old, diff_str = "NC", "NC" lines += ( @@ -1958,7 +1957,7 @@ def _check_docstring( if docstring is None: if not report_missing: return - lines = get_node_last_lineno(node) - node.lineno + 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 diff --git a/pylint/checkers/raw_metrics.py b/pylint/checkers/raw_metrics.py index 49ee2fa2f3..fe716d02cd 100644 --- a/pylint/checkers/raw_metrics.py +++ b/pylint/checkers/raw_metrics.py @@ -21,8 +21,8 @@ from pylint.checkers import BaseTokenChecker from pylint.exceptions import EmptyReportError from pylint.interfaces import ITokenChecker -from pylint.reporters import diff_string from pylint.reporters.ureports.nodes import Table +from pylint.utils import diff_string def report_raw_stats(sect, stats, old_stats): diff --git a/pylint/lint.py b/pylint/lint.py index 17a6f2f780..809e2294bd 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -78,7 +78,6 @@ from pylint.__pkginfo__ import version from pylint.constants import MSG_TYPES, OPTION_RGX from pylint.message import Message, MessagesHandlerMixIn, MessagesStore -from pylint.reporters.reports_handler_mix_in import ReportsHandlerMixIn from pylint.reporters.ureports import nodes as report_nodes from pylint.utils import ASTWalker, FileState, utils @@ -309,7 +308,7 @@ def _run_linter(self, file_or_module): class PyLinter( config.OptionsManagerMixIn, MessagesHandlerMixIn, - ReportsHandlerMixIn, + reporters.ReportsHandlerMixIn, checkers.BaseTokenChecker, ): """lint Python modules using external checkers. @@ -621,7 +620,7 @@ def __init__(self, options=(), reporter=None, option_groups=(), pylintrc=None): sys.version, ) MessagesHandlerMixIn.__init__(self) - ReportsHandlerMixIn.__init__(self) + reporters.ReportsHandlerMixIn.__init__(self) super(PyLinter, self).__init__( usage=__doc__, version=full_version, config_file=pylintrc or config.PYLINTRC ) diff --git a/pylint/reporters/__init__.py b/pylint/reporters/__init__.py index 98b0364a22..fe767bcc55 100644 --- a/pylint/reporters/__init__.py +++ b/pylint/reporters/__init__.py @@ -17,103 +17,12 @@ # For details: https://github.com/PyCQA/pylint/blob/master/COPYING """utilities methods and classes for reporters""" -from __future__ import print_function -import locale -import os -import sys -import warnings -CMPS = ["=", "-", "+"] - -# py3k has no more cmp builtin -if sys.version_info >= (3, 0): - - def cmp(a, b): # pylint: disable=redefined-builtin - return (a > b) - (a < b) - - -def diff_string(old, new): - """given an old and new int value, return a string representing the - difference - """ - diff = abs(old - new) - diff_str = "%s%s" % (CMPS[cmp(old, new)], diff and ("%.2f" % diff) or "") - return diff_str - - -class BaseReporter: - """base class for reporters - - symbols: show short symbolic names for messages. - """ - - extension = "" - - def __init__(self, output=None): - self.linter = None - self.section = 0 - self.out = None - self.out_encoding = None - self.set_output(output) - # Build the path prefix to strip to get relative paths - self.path_strip_prefix = os.getcwd() + os.sep - - def handle_message(self, msg): - """Handle a new message triggered on the current file.""" - - def set_output(self, output=None): - """set output stream""" - self.out = output or sys.stdout - - def writeln(self, string=""): - """write a line in the output buffer""" - print(string, file=self.out) - - def display_reports(self, layout): - """display results encapsulated in the layout tree""" - self.section = 0 - if hasattr(layout, "report_id"): - layout.children[0].children[0].data += " (%s)" % layout.report_id - self._display(layout) - - def _display(self, layout): - """display the layout""" - raise NotImplementedError() - - def display_messages(self, layout): - """Hook for displaying the messages of the reporter - - This will be called whenever the underlying messages - needs to be displayed. For some reporters, it probably - doesn't make sense to display messages as soon as they - are available, so some mechanism of storing them could be used. - This method can be implemented to display them after they've - been aggregated. - """ - - # Event callbacks - - def on_set_current_module(self, module, filepath): - """Hook called when a module starts to be analysed.""" - - def on_close(self, stats, previous_stats): - """Hook called when a module finished analyzing.""" - - -class CollectingReporter(BaseReporter): - """collects messages""" - - name = "collector" - - def __init__(self): - BaseReporter.__init__(self) - self.messages = [] - - def handle_message(self, msg): - self.messages.append(msg) - - _display = None +from pylint.reporters.base_reporter import BaseReporter +from pylint.reporters.collecting_reporter import CollectingReporter +from pylint.reporters.json_reporter import JSONReporter +from pylint.reporters.reports_handler_mix_in import ReportsHandlerMixIn def initialize(linter): @@ -121,3 +30,6 @@ def initialize(linter): from pylint import utils utils.register_plugins(linter, __path__[0]) + + +__all__ = ["BaseReporter", "ReportsHandlerMixIn", "JSONReporter", "CollectingReporter"] diff --git a/pylint/reporters/base_reporter.py b/pylint/reporters/base_reporter.py new file mode 100644 index 0000000000..aa0607f6dc --- /dev/null +++ b/pylint/reporters/base_reporter.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +# 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 __future__ import print_function + +import os +import sys + + +class BaseReporter: + """base class for reporters + + symbols: show short symbolic names for messages. + """ + + extension = "" + + def __init__(self, output=None): + self.linter = None + self.section = 0 + self.out = None + self.out_encoding = None + self.set_output(output) + # Build the path prefix to strip to get relative paths + self.path_strip_prefix = os.getcwd() + os.sep + + def handle_message(self, msg): + """Handle a new message triggered on the current file.""" + + def set_output(self, output=None): + """set output stream""" + self.out = output or sys.stdout + + def writeln(self, string=""): + """write a line in the output buffer""" + print(string, file=self.out) + + def display_reports(self, layout): + """display results encapsulated in the layout tree""" + self.section = 0 + if hasattr(layout, "report_id"): + layout.children[0].children[0].data += " (%s)" % layout.report_id + self._display(layout) + + def _display(self, layout): + """display the layout""" + raise NotImplementedError() + + def display_messages(self, layout): + """Hook for displaying the messages of the reporter + + This will be called whenever the underlying messages + needs to be displayed. For some reporters, it probably + doesn't make sense to display messages as soon as they + are available, so some mechanism of storing them could be used. + This method can be implemented to display them after they've + been aggregated. + """ + + # Event callbacks + + def on_set_current_module(self, module, filepath): + """Hook called when a module starts to be analysed.""" + + def on_close(self, stats, previous_stats): + """Hook called when a module finished analyzing.""" diff --git a/pylint/reporters/collecting_reporter.py b/pylint/reporters/collecting_reporter.py new file mode 100644 index 0000000000..7798d837e3 --- /dev/null +++ b/pylint/reporters/collecting_reporter.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +# 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 pylint.reporters.base_reporter import BaseReporter + + +class CollectingReporter(BaseReporter): + """collects messages""" + + name = "collector" + + def __init__(self): + BaseReporter.__init__(self) + self.messages = [] + + def handle_message(self, msg): + self.messages.append(msg) + + _display = None diff --git a/pylint/reporters/json.py b/pylint/reporters/json_reporter.py similarity index 97% rename from pylint/reporters/json.py rename to pylint/reporters/json_reporter.py index 18d46bd8f1..efac6f5c3f 100644 --- a/pylint/reporters/json.py +++ b/pylint/reporters/json_reporter.py @@ -14,7 +14,7 @@ import sys from pylint.interfaces import IReporter -from pylint.reporters import BaseReporter +from pylint.reporters.base_reporter import BaseReporter class JSONReporter(BaseReporter): diff --git a/pylint/test/test_self.py b/pylint/test/test_self.py index 1ef4d2f9b9..1d90845dd7 100644 --- a/pylint/test/test_self.py +++ b/pylint/test/test_self.py @@ -37,8 +37,7 @@ from pylint import utils from pylint.lint import Run -from pylint.reporters import BaseReporter -from pylint.reporters.json import JSONReporter +from pylint.reporters import BaseReporter, JSONReporter from pylint.reporters.text import * HERE = abspath(dirname(__file__)) diff --git a/pylint/test/unittest_reporters_json.py b/pylint/test/unittest_reporters_json.py index f3424ab669..19a215acff 100644 --- a/pylint/test/unittest_reporters_json.py +++ b/pylint/test/unittest_reporters_json.py @@ -15,7 +15,7 @@ from pylint import checkers from pylint.lint import PyLinter -from pylint.reporters.json import JSONReporter +from pylint.reporters import JSONReporter def test_simple_json_output(): diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index 67b56db3a8..310b830791 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -52,6 +52,7 @@ category_id, decoding_stream, deprecated_option, + diff_string, expand_modules, format_section, get_global_option, diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index e78cd98a43..08f6852b53 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -27,6 +27,24 @@ def normalize_text(text, line_len=80, indent=""): ) +CMPS = ["=", "-", "+"] + +# py3k has no more cmp builtin +if sys.version_info >= (3, 0): + + def cmp(a, b): # pylint: disable=redefined-builtin + return (a > b) - (a < b) + + +def diff_string(old, new): + """given an old and new int value, return a string representing the + difference + """ + diff = abs(old - new) + diff_str = "%s%s" % (CMPS[cmp(old, new)], diff and ("%.2f" % diff) or "") + return diff_str + + def get_module_and_frameid(node): """return the module name and the frame id in the module""" frame = node.frame() From c14f5a34bbc6b94721ef187f10ec77b3d5784956 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 21:32:39 +0100 Subject: [PATCH 0078/5147] Refactor - Create a new file for ASTWalker unittest --- pylint/test/utils/unittest_ast_walker.py | 69 ++++++++++++++++++++++++ pylint/test/utils/unittest_utils.py | 63 +--------------------- 2 files changed, 71 insertions(+), 61 deletions(-) create mode 100644 pylint/test/utils/unittest_ast_walker.py diff --git a/pylint/test/utils/unittest_ast_walker.py b/pylint/test/utils/unittest_ast_walker.py new file mode 100644 index 0000000000..b8fb78eacc --- /dev/null +++ b/pylint/test/utils/unittest_ast_walker.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- + +# 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 + +import warnings + +import astroid + +from pylint.checkers.utils import check_messages +from pylint.utils import ASTWalker + + +class TestASTWalker(object): + class MockLinter(object): + def __init__(self, msgs): + self._msgs = msgs + + def is_message_enabled(self, msgid): + return self._msgs.get(msgid, True) + + class Checker(object): + def __init__(self): + self.called = set() + + @check_messages("first-message") + def visit_module(self, module): + self.called.add("module") + + @check_messages("second-message") + def visit_call(self, module): + raise NotImplementedError + + @check_messages("second-message", "third-message") + def visit_assignname(self, module): + self.called.add("assignname") + + @check_messages("second-message") + def leave_assignname(self, module): + raise NotImplementedError + + def test_check_messages(self): + linter = self.MockLinter( + {"first-message": True, "second-message": False, "third-message": True} + ) + walker = ASTWalker(linter) + checker = self.Checker() + walker.add_checker(checker) + walker.walk(astroid.parse("x = func()")) + assert {"module", "assignname"} == checker.called + + def test_deprecated_methods(self): + class Checker(object): + def __init__(self): + self.called = False + + @check_messages("first-message") + def visit_assname(self, node): + self.called = True + + linter = self.MockLinter({"first-message": True}) + walker = ASTWalker(linter) + checker = Checker() + walker.add_checker(checker) + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + walker.walk(astroid.parse("x = 1")) + + assert not checker.called diff --git a/pylint/test/utils/unittest_utils.py b/pylint/test/utils/unittest_utils.py index 8ed2f26c92..c80cf2c9d7 100644 --- a/pylint/test/utils/unittest_utils.py +++ b/pylint/test/utils/unittest_utils.py @@ -18,70 +18,11 @@ import io import re -import warnings import astroid -from pylint.checkers.utils import check_messages, get_node_last_lineno -from pylint.utils import ASTWalker, utils - - -class TestASTWalker(object): - class MockLinter(object): - def __init__(self, msgs): - self._msgs = msgs - - def is_message_enabled(self, msgid): - return self._msgs.get(msgid, True) - - class Checker(object): - def __init__(self): - self.called = set() - - @check_messages("first-message") - def visit_module(self, module): - self.called.add("module") - - @check_messages("second-message") - def visit_call(self, module): - raise NotImplementedError - - @check_messages("second-message", "third-message") - def visit_assignname(self, module): - self.called.add("assignname") - - @check_messages("second-message") - def leave_assignname(self, module): - raise NotImplementedError - - def test_check_messages(self): - linter = self.MockLinter( - {"first-message": True, "second-message": False, "third-message": True} - ) - walker = ASTWalker(linter) - checker = self.Checker() - walker.add_checker(checker) - walker.walk(astroid.parse("x = func()")) - assert {"module", "assignname"} == checker.called - - def test_deprecated_methods(self): - class Checker(object): - def __init__(self): - self.called = False - - @check_messages("first-message") - def visit_assname(self, node): - self.called = True - - linter = self.MockLinter({"first-message": True}) - walker = ASTWalker(linter) - checker = Checker() - walker.add_checker(checker) - with warnings.catch_warnings(record=True): - warnings.simplefilter("always") - walker.walk(astroid.parse("x = 1")) - - assert not checker.called +from pylint.checkers.utils import get_node_last_lineno +from pylint.utils import utils def test__basename_in_blacklist_re_match(): From eabaf2eca73c8d3f210e7813bc68e8f3cc74676c Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 9 Mar 2019 22:39:57 +0100 Subject: [PATCH 0079/5147] Fix - Move checker.utils tests into their dedicated unittest It turns out there is not that much utils unittests. --- pylint/test/unittest_checkers_utils.py | 192 ++++++++++++++++++++++++ pylint/test/utils/unittest_utils.py | 195 ------------------------- 2 files changed, 192 insertions(+), 195 deletions(-) diff --git a/pylint/test/unittest_checkers_utils.py b/pylint/test/unittest_checkers_utils.py index 057859c2dc..74a3b6259c 100644 --- a/pylint/test/unittest_checkers_utils.py +++ b/pylint/test/unittest_checkers_utils.py @@ -185,3 +185,195 @@ class Second(datetime.datetime): #@ """ ) assert not utils.inherit_from_std_ex(node) + + +class TestGetNodeLastLineno: + def test_get_node_last_lineno_simple(self): + node = astroid.extract_node( + """ + pass + """ + ) + assert utils.get_node_last_lineno(node) == 2 + + def test_get_node_last_lineno_if_simple(self): + node = astroid.extract_node( + """ + if True: + print(1) + pass + """ + ) + assert utils.get_node_last_lineno(node) == 4 + + def test_get_node_last_lineno_if_elseif_else(self): + node = astroid.extract_node( + """ + if True: + print(1) + elif False: + print(2) + else: + print(3) + """ + ) + assert utils.get_node_last_lineno(node) == 7 + + def test_get_node_last_lineno_while(self): + node = astroid.extract_node( + """ + while True: + print(1) + """ + ) + assert utils.get_node_last_lineno(node) == 3 + + def test_get_node_last_lineno_while_else(self): + node = astroid.extract_node( + """ + while True: + print(1) + else: + print(2) + """ + ) + assert utils.get_node_last_lineno(node) == 5 + + def test_get_node_last_lineno_for(self): + node = astroid.extract_node( + """ + for x in range(0, 5): + print(1) + """ + ) + assert utils.get_node_last_lineno(node) == 3 + + def test_get_node_last_lineno_for_else(self): + node = astroid.extract_node( + """ + for x in range(0, 5): + print(1) + else: + print(2) + """ + ) + assert utils.get_node_last_lineno(node) == 5 + + def test_get_node_last_lineno_try(self): + node = astroid.extract_node( + """ + try: + print(1) + except ValueError: + print(2) + except Exception: + print(3) + """ + ) + assert utils.get_node_last_lineno(node) == 7 + + def test_get_node_last_lineno_try_except_else(self): + node = astroid.extract_node( + """ + try: + print(1) + except Exception: + print(2) + print(3) + else: + print(4) + """ + ) + assert utils.get_node_last_lineno(node) == 8 + + def test_get_node_last_lineno_try_except_finally(self): + node = astroid.extract_node( + """ + try: + print(1) + except Exception: + print(2) + finally: + print(4) + """ + ) + assert utils.get_node_last_lineno(node) == 7 + + def test_get_node_last_lineno_try_except_else_finally(self): + node = astroid.extract_node( + """ + try: + print(1) + except Exception: + print(2) + else: + print(3) + finally: + print(4) + """ + ) + assert utils.get_node_last_lineno(node) == 9 + + def test_get_node_last_lineno_with(self): + node = astroid.extract_node( + """ + with x as y: + print(1) + pass + """ + ) + assert utils.get_node_last_lineno(node) == 4 + + def test_get_node_last_lineno_method(self): + node = astroid.extract_node( + """ + def x(a, b): + print(a, b) + pass + """ + ) + assert utils.get_node_last_lineno(node) == 4 + + def test_get_node_last_lineno_decorator(self): + node = astroid.extract_node( + """ + @decor() + def x(a, b): + print(a, b) + pass + """ + ) + assert utils.get_node_last_lineno(node) == 5 + + def test_get_node_last_lineno_class(self): + node = astroid.extract_node( + """ + class C(object): + CONST = True + + def x(self, b): + print(b) + + def y(self): + pass + pass + """ + ) + assert utils.get_node_last_lineno(node) == 10 + + def test_get_node_last_lineno_combined(self): + node = astroid.extract_node( + """ + class C(object): + CONST = True + + def y(self): + try: + pass + except: + pass + finally: + pass + """ + ) + assert utils.get_node_last_lineno(node) == 11 diff --git a/pylint/test/utils/unittest_utils.py b/pylint/test/utils/unittest_utils.py index c80cf2c9d7..a009391def 100644 --- a/pylint/test/utils/unittest_utils.py +++ b/pylint/test/utils/unittest_utils.py @@ -19,9 +19,6 @@ import io import re -import astroid - -from pylint.checkers.utils import get_node_last_lineno from pylint.utils import utils @@ -52,195 +49,3 @@ def test_decoding_stream_known_encoding(): binary_io = io.BytesIO("€".encode("cp1252")) stream = utils.decoding_stream(binary_io, "cp1252") assert stream.read() == "€" - - -class TestGetNodeLastLineno: - def test_get_node_last_lineno_simple(self): - node = astroid.extract_node( - """ - pass - """ - ) - assert get_node_last_lineno(node) == 2 - - def test_get_node_last_lineno_if_simple(self): - node = astroid.extract_node( - """ - if True: - print(1) - pass - """ - ) - assert get_node_last_lineno(node) == 4 - - def test_get_node_last_lineno_if_elseif_else(self): - node = astroid.extract_node( - """ - if True: - print(1) - elif False: - print(2) - else: - print(3) - """ - ) - assert get_node_last_lineno(node) == 7 - - def test_get_node_last_lineno_while(self): - node = astroid.extract_node( - """ - while True: - print(1) - """ - ) - assert get_node_last_lineno(node) == 3 - - def test_get_node_last_lineno_while_else(self): - node = astroid.extract_node( - """ - while True: - print(1) - else: - print(2) - """ - ) - assert get_node_last_lineno(node) == 5 - - def test_get_node_last_lineno_for(self): - node = astroid.extract_node( - """ - for x in range(0, 5): - print(1) - """ - ) - assert get_node_last_lineno(node) == 3 - - def test_get_node_last_lineno_for_else(self): - node = astroid.extract_node( - """ - for x in range(0, 5): - print(1) - else: - print(2) - """ - ) - assert get_node_last_lineno(node) == 5 - - def test_get_node_last_lineno_try(self): - node = astroid.extract_node( - """ - try: - print(1) - except ValueError: - print(2) - except Exception: - print(3) - """ - ) - assert get_node_last_lineno(node) == 7 - - def test_get_node_last_lineno_try_except_else(self): - node = astroid.extract_node( - """ - try: - print(1) - except Exception: - print(2) - print(3) - else: - print(4) - """ - ) - assert get_node_last_lineno(node) == 8 - - def test_get_node_last_lineno_try_except_finally(self): - node = astroid.extract_node( - """ - try: - print(1) - except Exception: - print(2) - finally: - print(4) - """ - ) - assert get_node_last_lineno(node) == 7 - - def test_get_node_last_lineno_try_except_else_finally(self): - node = astroid.extract_node( - """ - try: - print(1) - except Exception: - print(2) - else: - print(3) - finally: - print(4) - """ - ) - assert get_node_last_lineno(node) == 9 - - def test_get_node_last_lineno_with(self): - node = astroid.extract_node( - """ - with x as y: - print(1) - pass - """ - ) - assert get_node_last_lineno(node) == 4 - - def test_get_node_last_lineno_method(self): - node = astroid.extract_node( - """ - def x(a, b): - print(a, b) - pass - """ - ) - assert get_node_last_lineno(node) == 4 - - def test_get_node_last_lineno_decorator(self): - node = astroid.extract_node( - """ - @decor() - def x(a, b): - print(a, b) - pass - """ - ) - assert get_node_last_lineno(node) == 5 - - def test_get_node_last_lineno_class(self): - node = astroid.extract_node( - """ - class C(object): - CONST = True - - def x(self, b): - print(b) - - def y(self): - pass - pass - """ - ) - assert get_node_last_lineno(node) == 10 - - def test_get_node_last_lineno_combined(self): - node = astroid.extract_node( - """ - class C(object): - CONST = True - - def y(self): - try: - pass - except: - pass - finally: - pass - """ - ) - assert get_node_last_lineno(node) == 11 From 9461dc082c7758d25b4bdd1b0725ffa5eec7de41 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 25 Dec 2018 17:54:23 +0100 Subject: [PATCH 0080/5147] Refactor - Move function for checker in Checker MsgStore.get_messages_from_checker => Checker.messages MsgStore.check_checker_consistency => Checker.check_consistency Probably makes more sense this way. --- pylint/checkers/__init__.py | 34 +++++++++++++ pylint/message/__init__.py | 2 +- pylint/message/build_message_definition.py | 2 +- pylint/message/message_handler_mix_in.py | 4 +- pylint/message/message_store.py | 49 ++----------------- pylint/test/message/unittest_message.py | 7 +-- pylint/test/message/unittest_message_store.py | 5 +- pylint/test/unittest_lint.py | 5 +- 8 files changed, 50 insertions(+), 58 deletions(-) diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py index 9f64bd68ba..bca38ec06e 100644 --- a/pylint/checkers/__init__.py +++ b/pylint/checkers/__init__.py @@ -42,7 +42,9 @@ from typing import Any from pylint.config import OptionsProviderMixIn +from pylint.exceptions import InvalidMessageError from pylint.interfaces import UNDEFINED +from pylint.message.build_message_definition import build_message_definition from pylint.utils import diff_string, register_plugins @@ -105,6 +107,38 @@ def add_message( """add a message of a given type""" self.linter.add_message(msg_id, 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) + + @property + def messages(self) -> list: + messages = [] + for msgid, msg_tuple in sorted(self.msgs.items()): + message = build_message_definition(self, msgid, msg_tuple) + messages.append(message) + return messages + # dummy methods implementing the IChecker interface def open(self): diff --git a/pylint/message/__init__.py b/pylint/message/__init__.py index 6b97d52819..430d066539 100644 --- a/pylint/message/__init__.py +++ b/pylint/message/__init__.py @@ -39,7 +39,7 @@ """All the classes related to Message handling.""" -from pylint.message.build_message_definition import build_message_def +from pylint.message.build_message_definition import build_message_definition from pylint.message.message import Message from pylint.message.message_definition import MessageDefinition from pylint.message.message_handler_mix_in import MessagesHandlerMixIn diff --git a/pylint/message/build_message_definition.py b/pylint/message/build_message_definition.py index fd3654abba..4de204d048 100644 --- a/pylint/message/build_message_definition.py +++ b/pylint/message/build_message_definition.py @@ -10,7 +10,7 @@ from pylint.message.message_definition import MessageDefinition -def build_message_def(checker, msgid, msg_tuple): +def build_message_definition(checker, msgid, msg_tuple): if implements(checker, (IRawChecker, ITokenChecker)): default_scope = WarningScope.LINE else: diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 240c62e572..79746f7272 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -20,7 +20,7 @@ ) from pylint.exceptions import InvalidMessageError, UnknownMessageError from pylint.interfaces import UNDEFINED -from pylint.message.build_message_definition import build_message_def +from pylint.message.build_message_definition import build_message_definition from pylint.message.message import Message from pylint.utils.utils import ( _format_option_value, @@ -446,7 +446,7 @@ def _print_checker_doc(checker_name, info, stream=None): for msgid, msg in sorted( msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) ): - msg = build_message_def(checker_name, msgid, msg) + msg = build_message_definition(checker_name, msgid, msg) print(msg.format_help(checkerref=False), file=stream) print("", file=stream) if reports: diff --git a/pylint/message/message_store.py b/pylint/message/message_store.py index 3c34b6f246..2bf6034325 100644 --- a/pylint/message/message_store.py +++ b/pylint/message/message_store.py @@ -8,7 +8,6 @@ import collections from pylint.exceptions import InvalidMessageError, UnknownMessageError -from pylint.message.build_message_definition import build_message_def class MessagesStore: @@ -44,29 +43,14 @@ def add_renamed_message(self, old_id, old_symbol, new_symbol): message_definition.old_names.append((old_id, old_symbol)) self._register_alternative_name(message_definition, old_id, old_symbol) - @staticmethod - def get_checker_message_definitions(checker): - """Return the list of messages definitions for a checker. - - :param BaseChecker checker: - :rtype: list - :return: A list of MessageDefinition. - """ - message_definitions = [] - for msgid, msg_tuple in sorted(checker.msgs.items()): - message = build_message_def(checker, msgid, msg_tuple) - message_definitions.append(message) - return message_definitions - def register_messages_from_checker(self, checker): """Register all messages from a checker. :param BaseChecker checker: """ - checker_message_definitions = self.get_checker_message_definitions(checker) - self._check_checker_consistency(checker_message_definitions) - for message_definition in checker_message_definitions: - self.register_message(message_definition) + checker.check_consistency() + for message in checker.messages: + self.register_message(message) def register_message(self, message): """Register a MessageDefinition with consistency in mind. @@ -84,33 +68,6 @@ def register_message(self, message): self._register_alternative_name(message, old_id, old_symbol) self._msgs_by_category[message.msgid[0]].append(message.msgid) - @staticmethod - def _check_checker_consistency(messages): - """Check the msgid consistency in a list of messages definitions. - - 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. - - :param list messages: List of MessageDefinition. - :raises InvalidMessageError: If the checker id in the messages are not - always the same - """ - checker_id = None - existing_ids = [] - for message in 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 _register_alternative_name(self, msg, msgid, symbol): """helper for register_message()""" self._check_id_and_symbol_consistency(msgid, symbol) diff --git a/pylint/test/message/unittest_message.py b/pylint/test/message/unittest_message.py index 311e4af192..7420ca273f 100644 --- a/pylint/test/message/unittest_message.py +++ b/pylint/test/message/unittest_message.py @@ -5,6 +5,7 @@ import pytest +from pylint.checkers import BaseChecker from pylint.exceptions import InvalidMessageError from pylint.message import MessageDefinition, MessagesStore @@ -116,7 +117,7 @@ def store(): ], ) def test_register_error(store, messages, expected): - class Checker(object): + class Checker(BaseChecker): name = "checker" msgs = messages @@ -126,11 +127,11 @@ class Checker(object): def test_register_error_new_id_duplicate_of_new(store): - class CheckerOne(object): + class CheckerOne(BaseChecker): name = "checker_one" msgs = {"W1234": ("message one", "msg-symbol-one", "msg description.")} - class CheckerTwo(object): + class CheckerTwo(BaseChecker): name = "checker_two" msgs = {"W1234": ("message two", "msg-symbol-two", "another msg description.")} diff --git a/pylint/test/message/unittest_message_store.py b/pylint/test/message/unittest_message_store.py index 09ff6d4c8f..fcb5434088 100644 --- a/pylint/test/message/unittest_message_store.py +++ b/pylint/test/message/unittest_message_store.py @@ -8,7 +8,8 @@ import pytest -from pylint.exceptions import InvalidMessageError, UnknownMessageError +from pylint.checkers import BaseChecker +from pylint.exceptions import UnknownMessageError from pylint.message import MessageDefinition, MessagesStore @@ -16,7 +17,7 @@ def store(): store = MessagesStore() - class Checker(object): + class Checker(BaseChecker): name = "achecker" msgs = { "W1234": ( diff --git a/pylint/test/unittest_lint.py b/pylint/test/unittest_lint.py index 1674fdf803..8d871bfafd 100644 --- a/pylint/test/unittest_lint.py +++ b/pylint/test/unittest_lint.py @@ -30,7 +30,7 @@ import re import sys import tempfile -from contextlib import contextmanager, redirect_stdout +from contextlib import contextmanager from importlib import reload from io import StringIO from os import chdir, getcwd @@ -47,9 +47,8 @@ MSG_STATE_SCOPE_CONFIG, MSG_STATE_SCOPE_MODULE, ) -from pylint.exceptions import InvalidMessageError, UnknownMessageError +from pylint.exceptions import InvalidMessageError from pylint.lint import ArgumentPreprocessingError, PyLinter, Run, preprocess_options -from pylint.message import MessageDefinition, MessagesStore from pylint.reporters import text from pylint.utils import FileState, tokenize_module From 1a666199d07d7baea82967bacbf98e0f9f65b5b3 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Mar 2019 09:35:53 +0100 Subject: [PATCH 0081/5147] Refactor - Create file for BaseChecker It was 'hidden' in checkers.__init__.py --- pylint/checkers/__init__.py | 94 +--------------- pylint/checkers/base_checker.py | 103 ++++++++++++++++++ pylint/test/message/unittest_message_store.py | 2 +- 3 files changed, 105 insertions(+), 94 deletions(-) create mode 100644 pylint/checkers/base_checker.py diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py index bca38ec06e..f040926bf5 100644 --- a/pylint/checkers/__init__.py +++ b/pylint/checkers/__init__.py @@ -39,12 +39,7 @@ """ -from typing import Any - -from pylint.config import OptionsProviderMixIn -from pylint.exceptions import InvalidMessageError -from pylint.interfaces import UNDEFINED -from pylint.message.build_message_definition import build_message_definition +from pylint.checkers.base_checker import BaseChecker, BaseTokenChecker from pylint.utils import diff_string, register_plugins @@ -69,93 +64,6 @@ def table_lines_from_stats(stats, old_stats, columns): return lines -class BaseChecker(OptionsProviderMixIn): - """base class for checkers""" - - # 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 ckecker 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 - - linter is an object implementing ILinter - """ - if self.name is not None: - self.name = self.name.lower() - OptionsProviderMixIn.__init__(self) - self.linter = linter - - def add_message( - self, - msg_id, - line=None, - node=None, - args=None, - confidence=UNDEFINED, - col_offset=None, - ): - """add a message of a given type""" - self.linter.add_message(msg_id, 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) - - @property - def messages(self) -> list: - messages = [] - for msgid, msg_tuple in sorted(self.msgs.items()): - message = build_message_definition(self, msgid, msg_tuple) - messages.append(message) - return messages - - # dummy methods implementing the IChecker interface - - 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() - - def initialize(linter): """initialize linter with checkers in this package """ register_plugins(linter, __path__[0]) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py new file mode 100644 index 0000000000..8c146e24c7 --- /dev/null +++ b/pylint/checkers/base_checker.py @@ -0,0 +1,103 @@ +# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) +# Copyright (c) 2013-2014 Google, Inc. +# Copyright (c) 2013 buck@yelp.com +# Copyright (c) 2014-2017 Claudiu Popa +# Copyright (c) 2014 Brett Cannon +# Copyright (c) 2014 Arun Persaud +# Copyright (c) 2015 Ionel Cristian Maries +# Copyright (c) 2016 Moises Lopez +# Copyright (c) 2017-2018 Bryce Guinta + +# 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 typing import Any + +from pylint.config import OptionsProviderMixIn +from pylint.exceptions import InvalidMessageError +from pylint.interfaces import UNDEFINED +from pylint.message import build_message_definition + + +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 ckecker 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 add_message( + self, + msgid, + line=None, + node=None, + args=None, + confidence=UNDEFINED, + col_offset=None, + ): + self.linter.add_message(msgid, line, node, args, confidence, col_offset) + + def check_consistency(self) -> None: + """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) + + @property + def messages(self) -> list: + messages = [] + for msgid, msg_tuple in sorted(self.msgs.items()): + message = build_message_definition(self, msgid, msg_tuple) + messages.append(message) + return messages + + # dummy methods implementing the IChecker interface + + 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/pylint/test/message/unittest_message_store.py b/pylint/test/message/unittest_message_store.py index fcb5434088..053bfca500 100644 --- a/pylint/test/message/unittest_message_store.py +++ b/pylint/test/message/unittest_message_store.py @@ -9,7 +9,7 @@ import pytest from pylint.checkers import BaseChecker -from pylint.exceptions import UnknownMessageError +from pylint.exceptions import InvalidMessageError, UnknownMessageError from pylint.message import MessageDefinition, MessagesStore From 0e81b797f8d852da50ec88a73fc9aa320c10a7ac Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 28 Mar 2019 21:16:16 +0100 Subject: [PATCH 0082/5147] Style - Use a single list comprehension instead of a for loop Co-Authored-By: Pierre-Sassoulas --- pylint/checkers/base_checker.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index 8c146e24c7..6ce8ecf9f1 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -80,11 +80,10 @@ def check_consistency(self) -> None: @property def messages(self) -> list: - messages = [] - for msgid, msg_tuple in sorted(self.msgs.items()): - message = build_message_definition(self, msgid, msg_tuple) - messages.append(message) - return messages + return [ + build_message_definition(self, msgid, msg_tuple) + for msgid, msg_tuple in sorted(self.msgs.items()) + ] # dummy methods implementing the IChecker interface From 293a7f5e6add850e2f127dd1680a6e43fb1a6949 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Thu, 28 Mar 2019 21:36:47 +0100 Subject: [PATCH 0083/5147] Refactor - Remove unused and untested code in utils There might be code that is used only in test but it's harder to detect. --- pylint/checkers/__init__.py | 12 +++--------- pylint/checkers/base.py | 11 +++-------- pylint/checkers/raw_metrics.py | 10 ++-------- pylint/utils/__init__.py | 1 - pylint/utils/utils.py | 18 ------------------ 5 files changed, 8 insertions(+), 44 deletions(-) diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py index f040926bf5..a8b6bac785 100644 --- a/pylint/checkers/__init__.py +++ b/pylint/checkers/__init__.py @@ -40,10 +40,10 @@ """ from pylint.checkers.base_checker import BaseChecker, BaseTokenChecker -from pylint.utils import diff_string, register_plugins +from pylint.utils import register_plugins -def table_lines_from_stats(stats, old_stats, columns): +def table_lines_from_stats(stats, _, columns): """get values listed in from and , and return a formated list of values, designed to be given to a ureport.Table object @@ -54,13 +54,7 @@ def table_lines_from_stats(stats, old_stats, columns): format = str # pylint: disable=redefined-builtin if isinstance(new, float): format = lambda num: "%.3f" % num - old = old_stats.get(m_type) - if old is not None: - diff_str = diff_string(old, new) - old = format(old) - else: - old, diff_str = "NC", "NC" - lines += (m_type.replace("_", " "), format(new), old, diff_str) + lines += (m_type.replace("_", " "), format(new), "NC", "NC") return lines diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 4e5c1a0bbb..16dd824c2b 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -349,7 +349,7 @@ def _has_abstract_methods(node): return len(utils.unimplemented_abstract_methods(node)) > 0 -def report_by_type_stats(sect, stats, old_stats): +def report_by_type_stats(sect, stats, _): """make a report of * percentage of different types documented @@ -378,16 +378,11 @@ def report_by_type_stats(sect, stats, old_stats): lines = ("type", "number", "old number", "difference", "%documented", "%badname") for node_type in ("module", "class", "method", "function"): new = stats[node_type] - old = old_stats.get(node_type, None) - if old is not None: - diff_str = lint_utils.diff_string(old, new) - else: - old, diff_str = "NC", "NC" lines += ( node_type, str(new), - str(old), - diff_str, + "NC", + "NC", nice_stats[node_type].get("percent_documented", "0"), nice_stats[node_type].get("percent_badname", "0"), ) diff --git a/pylint/checkers/raw_metrics.py b/pylint/checkers/raw_metrics.py index fe716d02cd..0564398f50 100644 --- a/pylint/checkers/raw_metrics.py +++ b/pylint/checkers/raw_metrics.py @@ -22,10 +22,9 @@ from pylint.exceptions import EmptyReportError from pylint.interfaces import ITokenChecker from pylint.reporters.ureports.nodes import Table -from pylint.utils import diff_string -def report_raw_stats(sect, stats, old_stats): +def report_raw_stats(sect, stats, _): """calculate percentage of code / doc / comment / empty """ total_lines = stats["total_lines"] @@ -37,12 +36,7 @@ def report_raw_stats(sect, stats, old_stats): key = node_type + "_lines" total = stats[key] percent = float(total * 100) / total_lines - old = old_stats.get(key, None) - if old is not None: - diff_str = diff_string(old, total) - else: - old, diff_str = "NC", "NC" - lines += (node_type, str(total), "%.2f" % percent, str(old), diff_str) + lines += (node_type, str(total), "%.2f" % percent, "NC", "NC") sect.append(Table(children=lines, cols=5, rheaders=1)) diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index 310b830791..67b56db3a8 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -52,7 +52,6 @@ category_id, decoding_stream, deprecated_option, - diff_string, expand_modules, format_section, get_global_option, diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index 08f6852b53..e78cd98a43 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -27,24 +27,6 @@ def normalize_text(text, line_len=80, indent=""): ) -CMPS = ["=", "-", "+"] - -# py3k has no more cmp builtin -if sys.version_info >= (3, 0): - - def cmp(a, b): # pylint: disable=redefined-builtin - return (a > b) - (a < b) - - -def diff_string(old, new): - """given an old and new int value, return a string representing the - difference - """ - diff = abs(old - new) - diff_str = "%s%s" % (CMPS[cmp(old, new)], diff and ("%.2f" % diff) or "") - return diff_str - - def get_module_and_frameid(node): """return the module name and the frame id in the module""" frame = node.frame() From a14fe17c097d5925f15c9164832aa339f9ad5d1e Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Thu, 28 Mar 2019 21:57:41 +0100 Subject: [PATCH 0084/5147] Fix - Remove the redefined build-in instead of using a pragma --- pylint/checkers/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py index a8b6bac785..9c6306f3a3 100644 --- a/pylint/checkers/__init__.py +++ b/pylint/checkers/__init__.py @@ -51,10 +51,8 @@ def table_lines_from_stats(stats, _, columns): lines = [] for m_type in columns: new = stats[m_type] - format = str # pylint: disable=redefined-builtin - if isinstance(new, float): - format = lambda num: "%.3f" % num - lines += (m_type.replace("_", " "), format(new), "NC", "NC") + new = "%.3f" % new if isinstance(new, float) else str(new) + lines += (m_type.replace("_", " "), new, "NC", "NC") return lines From b4721fb339c4f4f0b4e39a9d0cb3223b523ac0b2 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 3 Apr 2019 10:51:43 +0200 Subject: [PATCH 0085/5147] Better postponed evaluation of annotations handling Close #2847 --- ChangeLog | 4 ++++ pylint/checkers/variables.py | 9 ++++++++- pylint/test/functional/postponed_evaluation_activated.py | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 57254affa9..c41108a838 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Better postponed evaluation of annotations handling + + Close #2847 + * Support postponed evaluation of annotations for variable annotations. Close #2838 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 8590413167..345aa76dee 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1507,7 +1507,14 @@ def visit_name(self, node): # Handle postponed evaluation of annotations if not ( self._postponed_evaluation_enabled - and annotation_return + and isinstance( + stmt, + ( + astroid.AnnAssign, + astroid.FunctionDef, + astroid.Arguments, + ), + ) and name in node.root().locals ): self.add_message( diff --git a/pylint/test/functional/postponed_evaluation_activated.py b/pylint/test/functional/postponed_evaluation_activated.py index 6a150d84c8..3cc694d479 100644 --- a/pylint/test/functional/postponed_evaluation_activated.py +++ b/pylint/test/functional/postponed_evaluation_activated.py @@ -22,3 +22,7 @@ class Example: class Other: ... + + +class ExampleSelf: + next: ExampleSelf From c8db761f5fbe7535115639d578cd46c350d25b47 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 3 Apr 2019 11:22:27 +0200 Subject: [PATCH 0086/5147] Handle more `unnecessary-lambda` cases when dealing with additional kwargs in wrapped calls Close #2845 --- ChangeLog | 4 ++++ pylint/checkers/base.py | 14 ++++++++++++-- pylint/test/functional/unnecessary_lambda.py | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index c41108a838..0634fe387c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Handle more `unnecessary-lambda` cases when dealing with additional kwargs in wrapped calls + + Close #2845 + * Better postponed evaluation of annotations handling Close #2847 diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 16dd824c2b..64ca43f6fb 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -47,6 +47,7 @@ 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 @@ -1174,13 +1175,12 @@ def visit_lambda(self, node): # 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 - elif call.kwargs or call.keywords: - return if node.args.vararg: if self._has_variadic_argument(call.starargs, node.args.vararg): @@ -1188,6 +1188,16 @@ def visit_lambda(self, node): 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): diff --git a/pylint/test/functional/unnecessary_lambda.py b/pylint/test/functional/unnecessary_lambda.py index 461710919c..f0bd775f36 100644 --- a/pylint/test/functional/unnecessary_lambda.py +++ b/pylint/test/functional/unnecessary_lambda.py @@ -49,3 +49,5 @@ # Don't warn about this. _ = lambda: code().analysis() + +_ = lambda **kwargs: dict(bar=42, **kwargs) From 26c45aaf72e9e6596111867885a5e361eeea2303 Mon Sep 17 00:00:00 2001 From: Santiago Castro Date: Sun, 7 Apr 2019 16:45:49 -0400 Subject: [PATCH 0087/5147] Add missing closing parenthesis in option help --- pylint/checkers/typecheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index bd38e52220..d5981ab3b3 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -693,7 +693,7 @@ class should be ignored. A mixin class is detected if its name ends with \ "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 " + "deduced by static analysis). It supports qualified " "module names, as well as Unix pattern matching.", }, ), From 474f4082a91e451929fa629b4d76c06407cfde47 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Tue, 9 Apr 2019 15:48:19 +0200 Subject: [PATCH 0088/5147] When we can't infer bare except handlers, skip ``try-except-raise`` Close #2853 --- ChangeLog | 4 ++++ pylint/checkers/exceptions.py | 17 ++++++++++++++--- pylint/test/functional/try_except_raise.py | 9 --------- pylint/test/functional/try_except_raise.txt | 1 - 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0634fe387c..f5a59538ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* When we can't infer bare except handlers, skip ``try-except-raise`` + + Close #2853 + * Handle more `unnecessary-lambda` cases when dealing with additional kwargs in wrapped calls Close #2845 diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index 6521a5bd50..be4d7c9ac8 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -28,6 +28,7 @@ import typing import astroid +from astroid.node_classes import NodeNG from pylint import checkers, interfaces from pylint.checkers import utils @@ -405,7 +406,9 @@ def _check_catching_non_exception(self, handler, exc, part): ) def _check_try_except_raise(self, node): - def gather_exceptions_from_handler(handler): + def gather_exceptions_from_handler( + handler + ) -> typing.Optional[typing.List[NodeNG]]: exceptions = [] if handler.type: exceptions_in_handler = utils.safe_infer(handler.type) @@ -417,6 +420,9 @@ def gather_exceptions_from_handler(handler): } elif exceptions_in_handler: exceptions = [exceptions_in_handler] + else: + # Break when we cannot infer anything reliably. + return None return exceptions bare_raise = False @@ -429,9 +435,13 @@ def gather_exceptions_from_handler(handler): # 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) @@ -451,8 +461,9 @@ def gather_exceptions_from_handler(handler): bare_raise = True handler_having_bare_raise = handler excs_in_bare_handler = gather_exceptions_from_handler(handler) - if bare_raise: - self.add_message("try-except-raise", node=handler_having_bare_raise) + 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): diff --git a/pylint/test/functional/try_except_raise.py b/pylint/test/functional/try_except_raise.py index 2a9bed7998..c78852b814 100644 --- a/pylint/test/functional/try_except_raise.py +++ b/pylint/test/functional/try_except_raise.py @@ -96,14 +96,6 @@ def ddd(): except (OverflowError, ZeroDivisionError): print("a failure") - -try: - pass -except invalid_name: # [try-except-raise] - raise -except TypeError: - pass - try: pass except (FileNotFoundError, PermissionError): @@ -117,4 +109,3 @@ def ddd(): raise except (Exception,): print("a failure") - \ No newline at end of file diff --git a/pylint/test/functional/try_except_raise.txt b/pylint/test/functional/try_except_raise.txt index ccf8f11c69..882fd4007f 100644 --- a/pylint/test/functional/try_except_raise.txt +++ b/pylint/test/functional/try_except_raise.txt @@ -4,4 +4,3 @@ try-except-raise:53:ddd:The except handler raises immediately try-except-raise:67::The except handler raises immediately try-except-raise:72::The except handler raises immediately try-except-raise:94::The except handler raises immediately -try-except-raise:102::The except handler raises immediately From f029be7302a3e882e75255b2d9226361bbe53c94 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 11 Apr 2019 10:52:19 +0200 Subject: [PATCH 0089/5147] Fix the typing annotations --- pylint/checkers/exceptions.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index be4d7c9ac8..85c525e879 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -409,15 +409,17 @@ def _check_try_except_raise(self, node): def gather_exceptions_from_handler( handler ) -> typing.Optional[typing.List[NodeNG]]: - exceptions = [] + exceptions = [] # type: typing.List[NodeNG] if handler.type: exceptions_in_handler = utils.safe_infer(handler.type) if isinstance(exceptions_in_handler, astroid.Tuple): - exceptions = { - exception - for exception in exceptions_in_handler.elts - if isinstance(exception, astroid.Name) - } + exceptions = list( + { + exception + for exception in exceptions_in_handler.elts + if isinstance(exception, astroid.Name) + } + ) elif exceptions_in_handler: exceptions = [exceptions_in_handler] else: From fdc97772f4e5a61f69ae3e7bf5af27afbb9350a8 Mon Sep 17 00:00:00 2001 From: fadedDexofan Date: Fri, 19 Apr 2019 11:23:02 +1000 Subject: [PATCH 0090/5147] Added subprocess.run explicit set `check` argument checker. Closes #2848 --- CONTRIBUTORS.txt | 1 + ChangeLog | 4 ++++ doc/whatsnew/2.4.rst | 4 ++++ pylint/checkers/stdlib.py | 22 +++++++++++++++++++ .../test/functional/subprocess_run_check35.py | 6 +++++ .../test/functional/subprocess_run_check35.rc | 2 ++ .../functional/subprocess_run_check35.txt | 1 + 7 files changed, 40 insertions(+) create mode 100644 pylint/test/functional/subprocess_run_check35.py create mode 100644 pylint/test/functional/subprocess_run_check35.rc create mode 100644 pylint/test/functional/subprocess_run_check35.txt diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index cf08440f5f..3a9ade1bad 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -293,3 +293,4 @@ contributors: * Taewon Kim : contributor +* Daniil Kharkov: contributor diff --git a/ChangeLog b/ChangeLog index f5a59538ad..fbab5d5325 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Added `subprocess-run-check` to handle subrocess.run without explicitly set `check` keyword. + + Close #2848 + * When we can't infer bare except handlers, skip ``try-except-raise`` Close #2853 diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index 30fe87af04..e26856d2e9 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -13,6 +13,10 @@ Summary -- Release highlights New checkers ============ +* Added `subprocess-run-check` to handle subprocess.run without explicitly set `check` keyword. + + Close #2848 + * We added a new check message ``dict-iter-missing-items``. This is emitted when trying to iterate through a dict in a for loop without calling its .items() method. diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index 778ea98df2..ddd40cdf95 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -30,6 +30,8 @@ from pylint.checkers import BaseChecker, utils from pylint.interfaces import IAstroidChecker +PY35 = sys.version_info >= (3, 5) + OPEN_FILES = {"open", "file"} UNITTEST_CASE = "unittest.case" THREADING_THREAD = "threading.Thread" @@ -37,6 +39,7 @@ OS_ENVIRON = "os._Environ" ENV_GETTERS = {"os.getenv"} SUBPROCESS_POPEN = "subprocess.Popen" +SUBPROCESS_RUN = "subprocess.run" if sys.version_info >= (3, 0): OPEN_MODULE = "_io" @@ -147,6 +150,13 @@ class StdlibChecker(BaseChecker): "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 = { @@ -250,6 +260,15 @@ def _check_for_preexec_fn_in_Popen(self, node): if keyword.arg == "preexec_fn": self.add_message("subprocess-popen-preexec-fn", node=node) + def _check_for_check_kw_in_run(self, node): + if node.keywords: + kwargs = {keyword.arg: keyword.value for keyword in node.keywords} + else: + kwargs = {} + + 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(): @@ -266,6 +285,7 @@ def _check_shallow_copy_environ(self, node): "invalid-envvar-value", "invalid-envvar-default", "subprocess-popen-preexec-fn", + "subprocess-run-check", ) def visit_call(self, node): """Visit a Call node.""" @@ -289,6 +309,8 @@ def visit_call(self, node): self._check_shallow_copy_environ(node) elif name in ENV_GETTERS: self._check_env_function(node, inferred) + elif name == SUBPROCESS_RUN and PY35: + self._check_for_check_kw_in_run(node) self._check_deprecated_method(node, inferred) except astroid.InferenceError: return diff --git a/pylint/test/functional/subprocess_run_check35.py b/pylint/test/functional/subprocess_run_check35.py new file mode 100644 index 0000000000..f123fb20df --- /dev/null +++ b/pylint/test/functional/subprocess_run_check35.py @@ -0,0 +1,6 @@ +# pylint: disable=blacklisted-name,no-value-for-parameter,missing-docstring + +import subprocess + + +subprocess.run() # [subprocess-run-check] diff --git a/pylint/test/functional/subprocess_run_check35.rc b/pylint/test/functional/subprocess_run_check35.rc new file mode 100644 index 0000000000..71de8b6307 --- /dev/null +++ b/pylint/test/functional/subprocess_run_check35.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.5 diff --git a/pylint/test/functional/subprocess_run_check35.txt b/pylint/test/functional/subprocess_run_check35.txt new file mode 100644 index 0000000000..31e5dbfc10 --- /dev/null +++ b/pylint/test/functional/subprocess_run_check35.txt @@ -0,0 +1 @@ +subprocess-run-check:6::Using subprocess.run without explicitly set `check` is not recommended. From 6f45c7fdad77020eebc3474992c816aea522186e Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Fri, 19 Apr 2019 12:16:35 +0200 Subject: [PATCH 0091/5147] Squash a couple of lines into a single one --- pylint/checkers/stdlib.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index ddd40cdf95..8aac66a062 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -261,11 +261,7 @@ def _check_for_preexec_fn_in_Popen(self, node): self.add_message("subprocess-popen-preexec-fn", node=node) def _check_for_check_kw_in_run(self, node): - if node.keywords: - kwargs = {keyword.arg: keyword.value for keyword in node.keywords} - else: - kwargs = {} - + kwargs = {keyword.arg for keyword in (node.keywords or ())} if "check" not in kwargs: self.add_message("subprocess-run-check", node=node) From 377e82fdf21c74fe497217bfc97f696b36ead233 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Fri, 19 Apr 2019 12:17:10 +0200 Subject: [PATCH 0092/5147] Rename a function to use only lowercase characters --- pylint/checkers/stdlib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index 8aac66a062..c57f90e577 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -254,7 +254,7 @@ 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): + def _check_for_preexec_fn_in_popen(self, node): if node.keywords: for keyword in node.keywords: if keyword.arg == "preexec_fn": @@ -298,7 +298,7 @@ def visit_call(self, node): if inferred.qname() == THREADING_THREAD: self._check_bad_thread_instantiation(node) elif inferred.qname() == SUBPROCESS_POPEN: - self._check_for_preexec_fn_in_Popen(node) + self._check_for_preexec_fn_in_popen(node) elif isinstance(inferred, astroid.FunctionDef): name = inferred.qname() if name == COPY_COPY: From b1ee3854ab89786bdfb53b062ffc3e56080ec4a2 Mon Sep 17 00:00:00 2001 From: Zeb Nicholls Date: Mon, 29 Apr 2019 18:18:25 +1000 Subject: [PATCH 0093/5147] Add 'of' to GoogleDocstring multiple type (#2884) A docstring of the following form should pass (see https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_numpy.html, search for ':obj:list of :obj:str') def my_func(self): """This is a docstring. Returns ------- :obj:`list` of :obj:`str` List of strings """ return ["hi", "bye"] #@ --- CONTRIBUTORS.txt | 3 +++ ChangeLog | 2 ++ doc/whatsnew/2.4.rst | 14 ++++++++++++++ pylint/extensions/_check_docs_utils.py | 2 +- pylint/test/extensions/test_check_return_docs.py | 15 +++++++++++++++ 5 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 3a9ade1bad..b6868cadc6 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -294,3 +294,6 @@ contributors: * Taewon Kim : contributor * Daniil Kharkov: contributor + +* Zeb Nicholls: contributor + - Made W9011 compatible with 'of' syntax in return types diff --git a/ChangeLog b/ChangeLog index fbab5d5325..ceba7f52ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,8 @@ What's New in Pylint 2.4.0? Release date: TBA +* Allow of in `GoogleDocstring.re_multiple_type` + * Added `subprocess-run-check` to handle subrocess.run without explicitly set `check` keyword. Close #2848 diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index e26856d2e9..80a05f8330 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -91,3 +91,17 @@ The following imports do not trigger an ``ungrouped-imports`` anymore :: import zipfile from unittest import TestCase from unittest.mock import MagicMock + +* The checker for missing return documentation is now more flexible. + +The following does not trigger a ``missing-return-doc`` anymore :: + + def my_func(self): + """This is a docstring. + + Returns + ------- + :obj:`list` of :obj:`str` + List of strings + """ + return ["hi", "bye"] #@ diff --git a/pylint/extensions/_check_docs_utils.py b/pylint/extensions/_check_docs_utils.py index 2f45e6a4c2..1bbfc9755a 100644 --- a/pylint/extensions/_check_docs_utils.py +++ b/pylint/extensions/_check_docs_utils.py @@ -443,7 +443,7 @@ class GoogleDocstring(Docstring): re_multiple_type = r""" (?:{container_type}|{type}|{xref}) - (?:\s+or\s+(?:{container_type}|{type}|{xref}))* + (?:\s+(?:of|or)\s+(?:{container_type}|{type}|{xref}))* """.format( type=re_type, xref=re_xref, container_type=re_container_type ) diff --git a/pylint/test/extensions/test_check_return_docs.py b/pylint/test/extensions/test_check_return_docs.py index 29de723101..a6d12e501c 100644 --- a/pylint/test/extensions/test_check_return_docs.py +++ b/pylint/test/extensions/test_check_return_docs.py @@ -250,6 +250,21 @@ def my_func(self): with self.assertNoMessages(): self.checker.visit_return(return_node) + def test_find_numpy_returns_with_of(self): + return_node = astroid.extract_node(''' + def my_func(self): + """This is a docstring. + + Returns + ------- + :obj:`list` of :obj:`str` + List of strings + """ + return ["hi", "bye"] #@ + ''') + with self.assertNoMessages(): + self.checker.visit_return(return_node) + def test_ignores_sphinx_return_none(self): return_node = astroid.extract_node(''' def my_func(self, doc_type): From 8d237a0ae3e28fb64b7867a4c61f8365fe7bbd5b Mon Sep 17 00:00:00 2001 From: Tyler Thieding Date: Mon, 29 Apr 2019 04:21:52 -0400 Subject: [PATCH 0094/5147] Add broad try clause extension. (#2890) Add an extension checker (pylint.extensions.broad_try_clause) that enforces a configurable maximum number of statements inside of a try clause. This facilitates enforcing PEP 8's guidelines about try/except statements and the amount of code in the try clause: "Additionally, for all try/except clauses, limit the try clause to the absolute minimum amount of code necessary. Again, this avoids masking bugs." --- CONTRIBUTORS.txt | 2 + ChangeLog | 4 ++ pylint/extensions/broad_try_clause.py | 59 +++++++++++++++++++ .../test/extensions/data/broad_try_clause.py | 14 +++++ .../test/extensions/test_broad_try_clause.py | 52 ++++++++++++++++ 5 files changed, 131 insertions(+) create mode 100644 pylint/extensions/broad_try_clause.py create mode 100644 pylint/test/extensions/data/broad_try_clause.py create mode 100644 pylint/test/extensions/test_broad_try_clause.py diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index b6868cadc6..ea603452b2 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -295,5 +295,7 @@ contributors: * Daniil Kharkov: contributor +* Tyler N. Thieding: contributor + * Zeb Nicholls: contributor - Made W9011 compatible with 'of' syntax in return types diff --git a/ChangeLog b/ChangeLog index ceba7f52ee..59b18f0ef0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -97,6 +97,10 @@ Release date: TBA Close #2806 +* Added new extension to detect too much code in a try clause + + Close #2877 + What's New in Pylint 2.3.0? =========================== diff --git a/pylint/extensions/broad_try_clause.py b/pylint/extensions/broad_try_clause.py new file mode 100644 index 0000000000..0bae2b8fd6 --- /dev/null +++ b/pylint/extensions/broad_try_clause.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019 Tyler N. Thieding + +# 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 try/except statements with too much code in the try clause.""" + +from pylint import checkers, interfaces + + +class BroadTryClauseChecker(checkers.BaseChecker): + """Checks for try clauses with too many lines. + + According to PEP 8, ``try`` clauses shall contain the absolute minimum + amount of code. This checker enforces a maximum number of statements within + ``try`` clauses. + + """ + + __implements__ = interfaces.IAstroidChecker + + # configuration section name + name = "broad_try_clause" + msgs = { + "W0717": ( + "%s", + "too-many-try-statements", + "Try clause contains too many statements.", + ) + } + + priority = -2 + options = ( + ( + "max-try-statements", + { + "default": 1, + "type": "int", + "metavar": "", + "help": "Maximum number of statements allowed in a try clause", + }, + ), + ) + + def visit_tryexcept(self, node): + try_clause_statements = len(node.body) + if try_clause_statements > self.config.max_try_statements: + msg = "try clause contains {0} statements, at most {1} expected".format( + try_clause_statements, self.config.max_try_statements + ) + self.add_message( + "too-many-try-statements", node.lineno, node=node, args=msg + ) + + +def register(linter): + """Required method to auto register this checker.""" + linter.register_checker(BroadTryClauseChecker(linter)) diff --git a/pylint/test/extensions/data/broad_try_clause.py b/pylint/test/extensions/data/broad_try_clause.py new file mode 100644 index 0000000000..4d65302c87 --- /dev/null +++ b/pylint/test/extensions/data/broad_try_clause.py @@ -0,0 +1,14 @@ +# pylint: disable=missing-docstring, invalid-name + +MY_DICTIONARY = {"key_one": 1, "key_two": 2, "key_three": 3} + +try: # [max-try-statements] + value = MY_DICTIONARY["key_one"] + value += 1 +except KeyError: + pass + +try: + value = MY_DICTIONARY["key_one"] +except KeyError: + value = 0 diff --git a/pylint/test/extensions/test_broad_try_clause.py b/pylint/test/extensions/test_broad_try_clause.py new file mode 100644 index 0000000000..921c58def6 --- /dev/null +++ b/pylint/test/extensions/test_broad_try_clause.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019 Tyler N. Thieding + +# 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 + +"""Tests for the pylint checker in :mod:`pylint.extensions.broad_try_clause +""" + +import os.path as osp +import unittest + +from pylint import checkers +from pylint.extensions.broad_try_clause import BroadTryClauseChecker +from pylint.lint import PyLinter +from pylint.reporters import BaseReporter + + +class BroadTryClauseTestReporter(BaseReporter): + def handle_message(self, msg): + self.messages.append(msg) + + def on_set_current_module(self, module, filepath): + self.messages = [] + + +class BroadTryClauseTC(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls._linter = PyLinter() + cls._linter.set_reporter(BroadTryClauseTestReporter()) + checkers.initialize(cls._linter) + cls._linter.register_checker(BroadTryClauseChecker(cls._linter)) + cls._linter.disable("I") + + def test_broad_try_clause_message(self): + elif_test = osp.join( + osp.dirname(osp.abspath(__file__)), "data", "broad_try_clause.py" + ) + self._linter.check([elif_test]) + msgs = self._linter.reporter.messages + self.assertEqual(len(msgs), 1) + + self.assertEqual(msgs[0].symbol, "too-many-try-statements") + self.assertEqual( + msgs[0].msg, "try clause contains 2 statements, at most 1 expected" + ) + self.assertEqual(msgs[0].line, 5) + + +if __name__ == "__main__": + unittest.main() From 5f658150ffe257b61ee4240f30bfd1991435d1dd Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 29 Apr 2019 10:24:39 +0200 Subject: [PATCH 0095/5147] Add a what's new entry for the new extension --- doc/whatsnew/2.4.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index 80a05f8330..42fe36c2c5 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -46,6 +46,17 @@ New checkers to make ``pylint`` suggest using ``defusedxml`` instead of ``xml`` and ``ujson`` rather than ``json``. +* A new extension ``broad_try_clause`` was added. + + This extension enforces a configurable maximum number of statements inside + of a try clause. This facilitates enforcing PEP 8's guidelines about try / except + statements and the amount of code in the try clause. + + You can enable this extension using ``--load-plugins=pylint.extensions.broad_try_clause`` + and you can configure the amount of statements in a try statement using + ``--max-try-statements``. + + Other Changes ============= @@ -91,7 +102,7 @@ The following imports do not trigger an ``ungrouped-imports`` anymore :: import zipfile from unittest import TestCase from unittest.mock import MagicMock - + * The checker for missing return documentation is now more flexible. The following does not trigger a ``missing-return-doc`` anymore :: From 2bf5c61a3ff6ae90613b81679de42c0f19aea600 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 29 Apr 2019 10:25:32 +0200 Subject: [PATCH 0096/5147] Flip around the message of broad_try_clause --- pylint/extensions/broad_try_clause.py | 2 +- pylint/test/extensions/test_broad_try_clause.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pylint/extensions/broad_try_clause.py b/pylint/extensions/broad_try_clause.py index 0bae2b8fd6..9a61fb6487 100644 --- a/pylint/extensions/broad_try_clause.py +++ b/pylint/extensions/broad_try_clause.py @@ -46,7 +46,7 @@ class BroadTryClauseChecker(checkers.BaseChecker): def visit_tryexcept(self, node): try_clause_statements = len(node.body) if try_clause_statements > self.config.max_try_statements: - msg = "try clause contains {0} statements, at most {1} expected".format( + msg = "try clause contains {0} statements, expected at most {1}".format( try_clause_statements, self.config.max_try_statements ) self.add_message( diff --git a/pylint/test/extensions/test_broad_try_clause.py b/pylint/test/extensions/test_broad_try_clause.py index 921c58def6..21b591dc15 100644 --- a/pylint/test/extensions/test_broad_try_clause.py +++ b/pylint/test/extensions/test_broad_try_clause.py @@ -43,7 +43,7 @@ def test_broad_try_clause_message(self): self.assertEqual(msgs[0].symbol, "too-many-try-statements") self.assertEqual( - msgs[0].msg, "try clause contains 2 statements, at most 1 expected" + msgs[0].msg, "try clause contains 2 statements, expected at most 1" ) self.assertEqual(msgs[0].line, 5) From c26bd45edb74f1857471fcd134c8443058670790 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 30 Apr 2019 02:08:25 -0700 Subject: [PATCH 0097/5147] fix old-division check for int(...) (#2892) --- .pre-commit-config.yaml | 1 - pylint/checkers/python3.py | 14 +++++++----- pylint/test/unittest_checker_python3.py | 29 +++++++++++++++++++------ 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 11890aa26b..23c155d065 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,6 @@ repos: - id: black args: [--safe, --quiet] exclude: functional|input|test/extension|test/regrtest_data|test/data - python_version: python3.6 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.1.0 hooks: diff --git a/pylint/checkers/python3.py b/pylint/checkers/python3.py index 5ee6cdceb4..69776fbcaf 100644 --- a/pylint/checkers/python3.py +++ b/pylint/checkers/python3.py @@ -1069,14 +1069,18 @@ 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 a numeric one, bail out. + # 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, float)) + ( + isinstance(inferred, astroid.Const) + and isinstance(inferred.value, int) + ) + or ( + isinstance(inferred, astroid.Instance) + and inferred.name == "int" + ) ): break - if isinstance(arg, astroid.Const) and isinstance(arg.value, float): - break else: self.add_message("old-division", node=node) diff --git a/pylint/test/unittest_checker_python3.py b/pylint/test/unittest_checker_python3.py index 417bc2bb6b..2fa8fca295 100644 --- a/pylint/test/unittest_checker_python3.py +++ b/pylint/test/unittest_checker_python3.py @@ -456,10 +456,19 @@ def test(): self.checker.visit_importfrom(node) def test_division(self): - node = astroid.extract_node("3 / 2 #@") - message = testutils.Message("old-division", node=node) - with self.assertAddsMessages(message): - self.checker.visit_binop(node) + nodes = astroid.extract_node( + """\ + from _unknown import a, b + 3 / 2 #@ + 3 / int(a) #@ + int(a) / 3 #@ + a / b #@ + """ + ) + for node in nodes: + message = testutils.Message("old-division", node=node) + with self.assertAddsMessages(message): + self.checker.visit_binop(node) def test_division_with_future_statement(self): module = astroid.parse("from __future__ import division; 3 / 2") @@ -472,10 +481,16 @@ def test_floor_division(self): self.checker.visit_binop(node) def test_division_by_float(self): - left_node = astroid.extract_node("3.0 / 2 #@") - right_node = astroid.extract_node(" 3 / 2.0 #@") + nodes = astroid.extract_node( + """\ + 3.0 / 2 #@ + 3 / 2.0 #@ + 3 / float(a) #@ + float(a) / 3 #@ + """ + ) with self.assertNoMessages(): - for node in (left_node, right_node): + for node in nodes: self.checker.visit_binop(node) def test_division_different_types(self): From 7054204c688947e6c056cde27190951bfe8f63e9 Mon Sep 17 00:00:00 2001 From: Martin Vielsmaier Date: Sun, 5 May 2019 10:16:07 +0200 Subject: [PATCH 0098/5147] Fix crash in callable check (#2901) --- CONTRIBUTORS.txt | 2 ++ ChangeLog | 2 ++ pylint/checkers/typecheck.py | 3 ++- pylint/test/unittest_checker_typecheck.py | 16 ++++++++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index ea603452b2..d01b830aa0 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -299,3 +299,5 @@ contributors: * Zeb Nicholls: contributor - Made W9011 compatible with 'of' syntax in return types + +* Martin Vielsmaier: contributor diff --git a/ChangeLog b/ChangeLog index 59b18f0ef0..93192a4731 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,8 @@ What's New in Pylint 2.4.0? Release date: TBA +* Fix crash happening when parent of called object cannot be determined + * Allow of in `GoogleDocstring.re_multiple_type` * Added `subprocess-run-check` to handle subrocess.run without explicitly set `check` keyword. diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index d5981ab3b3..1b96d5d266 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -1051,7 +1051,8 @@ def visit_call(self, node): if isinstance(called, astroid.Instance) and ( not has_known_bases(called) or ( - isinstance(called.scope(), astroid.ClassDef) + called.parent is not None + and isinstance(called.scope(), astroid.ClassDef) and "__get__" in called.locals ) ): diff --git a/pylint/test/unittest_checker_typecheck.py b/pylint/test/unittest_checker_typecheck.py index 7bce396c5c..ba61a7d135 100644 --- a/pylint/test/unittest_checker_typecheck.py +++ b/pylint/test/unittest_checker_typecheck.py @@ -332,3 +332,19 @@ class AggregateCls: ) with self.assertNoMessages(): self.checker.visit_call(call) + + def test_unknown_parent(self): + """Make sure the callable check does not crash when a node's parent + cannot be determined. + """ + call = astroid.extract_node( + """ + def get_num(n): + return 2 * n + get_num(10)() + """ + ) + with self.assertAddsMessages( + Message("not-callable", node=call, args="get_num(10)") + ): + self.checker.visit_call(call) From eb3761504b86d6caf06feae2072f00f846e70435 Mon Sep 17 00:00:00 2001 From: agutole Date: Tue, 14 May 2019 06:24:15 -0300 Subject: [PATCH 0099/5147] Correct word pointed when the same word has more than one error in the same line Fixes #2895 --- CONTRIBUTORS.txt | 2 + ChangeLog | 4 ++ pylint/checkers/spelling.py | 14 ++++-- pylint/test/unittest_checker_spelling.py | 57 ++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index d01b830aa0..2dcbc9d90e 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -301,3 +301,5 @@ contributors: - Made W9011 compatible with 'of' syntax in return types * Martin Vielsmaier: contributor + +* Agustin Toledo: contributor diff --git a/ChangeLog b/ChangeLog index 93192a4731..6ce1d8bfc9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Fix pointer on spelling check when the error are more than one time in the same line. + + Close #2895 + * Fix crash happening when parent of called object cannot be determined * Allow of in `GoogleDocstring.re_multiple_type` diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py index c1a0eb2de3..f04c0a6c5c 100644 --- a/pylint/checkers/spelling.py +++ b/pylint/checkers/spelling.py @@ -284,12 +284,17 @@ def close(self): 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, _ in self.tokenizer(line.strip()): + 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. @@ -328,12 +333,15 @@ def _check_spelling(self, msgid, line, line_num): suggestions = self.spelling_dict.suggest(word) del suggestions[self.config.max_spelling_suggestions :] - m = re.search(r"(\W|^)(%s)(\W|$)" % word, line) + line_segment = line[word_start_at:] + m = re.search(r"(\W|^)(%s)(\W|$)" % word, line_segment) if m: # Start position of second group in regex. col = m.regs[2][0] else: - col = line.index(word) + col = line_segment.index(word) + + col += word_start_at if starts_with_comment: col += 1 diff --git a/pylint/test/unittest_checker_spelling.py b/pylint/test/unittest_checker_spelling.py index 8abd85577a..c1777f74ff 100644 --- a/pylint/test/unittest_checker_spelling.py +++ b/pylint/test/unittest_checker_spelling.py @@ -303,3 +303,60 @@ class ComentAbc(object): ) ): self.checker.visit_classdef(stmt) + + @skip_on_missing_package_or_dict + @set_config(spelling_dict=spell_dict) + def test_more_than_one_error_in_same_line_for_same_word_on_docstring(self): + stmt = astroid.extract_node( + 'class ComentAbc(object):\n """Check teh dummy comment teh"""\n pass' + ) + with self.assertAddsMessages( + Message( + "wrong-spelling-in-docstring", + line=2, + args=( + "teh", + "Check teh dummy comment teh", + " ^^^", + self._get_msg_suggestions("teh"), + ), + ), + Message( + "wrong-spelling-in-docstring", + line=2, + args=( + "teh", + "Check teh dummy comment teh", + " ^^^", + self._get_msg_suggestions("teh"), + ), + ), + ): + self.checker.visit_classdef(stmt) + + @skip_on_missing_package_or_dict + @set_config(spelling_dict=spell_dict) + def test_more_than_one_error_in_same_line_for_same_word_on_comment(self): + with self.assertAddsMessages( + Message( + "wrong-spelling-in-comment", + line=1, + args=( + "coment", + "# bad coment coment", + " ^^^^^^", + self._get_msg_suggestions("coment"), + ), + ), + Message( + "wrong-spelling-in-comment", + line=1, + args=( + "coment", + "# bad coment coment", + " ^^^^^^", + self._get_msg_suggestions("coment"), + ), + ), + ): + self.checker.process_tokens(_tokenize_str("# bad coment coment")) From b488ce54e30762d81c9a7eb5c43ed904ee91cf2c Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 19 May 2019 10:02:52 +0200 Subject: [PATCH 0100/5147] Exclude ``__dict__`` from ``attribute-defined-outside-init`` Close #2909 --- ChangeLog | 7 ++++++- pylint/checkers/classes.py | 3 +++ pylint/test/functional/attribute_defined_outside_init.py | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 6ce1d8bfc9..a3408dea60 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,7 +7,12 @@ What's New in Pylint 2.4.0? Release date: TBA -* Fix pointer on spelling check when the error are more than one time in the same line. + +* Exclude ``__dict__`` from ``attribute-defined-outside-init`` + + Close #2909 + +* Fix pointer on spelling check when the error are more than one time in the same line. Close #2895 diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index d75107136c..604b8ef452 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -823,6 +823,9 @@ def leave_classdef(self, cnode): # check if any method attr is defined in is a defining method if any(node.frame().name in defining_methods for node in nodes): continue + # Exclude `__dict__` as it is already defined. + if attr == "__dict__": + continue # check attribute is defined in a parent's __init__ for parent in cnode.instance_attr_ancestors(attr): diff --git a/pylint/test/functional/attribute_defined_outside_init.py b/pylint/test/functional/attribute_defined_outside_init.py index 826b5d68b2..ab5cf5f192 100644 --- a/pylint/test/functional/attribute_defined_outside_init.py +++ b/pylint/test/functional/attribute_defined_outside_init.py @@ -60,3 +60,8 @@ def test_mixin(self): """Don't emit attribute-defined-outside-init for mixin classes.""" if self.defined_already: # pylint: disable=access-member-before-definition self.defined_already = None + + +class F: + def func(self): + self.__dict__ = {'foo': 'bar'} From c58dfdd557530f396f213eb5777df7a3c61dd08a Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 19 May 2019 11:14:12 +0200 Subject: [PATCH 0101/5147] Add test to demonstrate that a recursion error does not happen. Close #2906 --- pylint/test/functional/recursion_error_2906.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pylint/test/functional/recursion_error_2906.py diff --git a/pylint/test/functional/recursion_error_2906.py b/pylint/test/functional/recursion_error_2906.py new file mode 100644 index 0000000000..062109672f --- /dev/null +++ b/pylint/test/functional/recursion_error_2906.py @@ -0,0 +1,14 @@ +"""Recursion error for https://github.com/PyCQA/pylint/issues/2906""" +# pylint: disable=blacklisted-name,global-statement,invalid-name,missing-docstring +lst = [] + + +def foo(): + lst.append(0) + + +def bar(): + global lst + + length = len(lst) + lst += [length] From 312dde3946433e36dfe2f2c5ba1229374a302932 Mon Sep 17 00:00:00 2001 From: Nick Smith Date: Sun, 19 May 2019 06:13:09 -0400 Subject: [PATCH 0102/5147] Fix misspellings in documentation (#2922) --- CONTRIBUTORS.txt | 2 ++ ChangeLog | 5 +---- doc/conf.py | 2 +- pylint/checkers/imports.py | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 2dcbc9d90e..8d885176b8 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -303,3 +303,5 @@ contributors: * Martin Vielsmaier: contributor * Agustin Toledo: contributor + +* Nicholas Smith: contributor diff --git a/ChangeLog b/ChangeLog index a3408dea60..05c68c6a40 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,11 +7,8 @@ What's New in Pylint 2.4.0? Release date: TBA - * Exclude ``__dict__`` from ``attribute-defined-outside-init`` - Close #2909 - * Fix pointer on spelling check when the error are more than one time in the same line. Close #2895 @@ -918,7 +915,7 @@ Release date: 2018-07-15 Close #2214 - * Fix false-postive undefined-variable in nested lambda + * Fix false-positive undefined-variable in nested lambda Close #760 diff --git a/doc/conf.py b/doc/conf.py index b214a14ccb..c89917ddb2 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -239,5 +239,5 @@ } # Prevent label issues due to colliding section names -# through including mulitple documents +# through including multiple documents autosectionlabel_prefix_document = True diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index 1405629fea..20b02aea7c 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -928,7 +928,7 @@ def _report_dependencies_graph(self, sect, _, _dummy): _make_graph(filename, self._internal_dependencies_info(), sect, "internal ") def _filter_dependencies_graph(self, internal): - """build the internal or the external depedency graph""" + """build the internal or the external dependency graph""" graph = collections.defaultdict(set) for importee, importers in self.stats["dependencies"].items(): for importer in importers: From edfb78ffbcdcca2bf0351ab77d56f0465c52fdbf Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 19 May 2019 12:23:18 +0200 Subject: [PATCH 0103/5147] Pass the line number for bad-inline-option instead of passing the actual line Close #2904 --- pylint/checkers/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylint/checkers/misc.py b/pylint/checkers/misc.py index 882d8ab2fc..d4da9f33bd 100644 --- a/pylint/checkers/misc.py +++ b/pylint/checkers/misc.py @@ -160,7 +160,7 @@ def process_tokens(self, tokens): self.add_message( "bad-inline-option", args=disable_option_match.group(1).strip(), - line=comment.string, + line=comment.start[0], ) continue From fc569a61d79a13fb237e472c5e95ee45bbac6707 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 19 May 2019 12:31:16 +0200 Subject: [PATCH 0104/5147] Remove inadvertent change --- pylint/checkers/format.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index 37963e407c..bc935ddd06 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -1333,9 +1333,3 @@ def check_indent_level(self, string, expected, line_num): def register(linter): """required method to auto register this checker """ linter.register_checker(FormatChecker(linter)) - - -def funkier(): - for val in range(3): - pass - print(val) From 36f206b5856d384c1531a41a7c73099859b99e71 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 20 May 2019 08:53:39 +0200 Subject: [PATCH 0105/5147] tox stopped installing the latest pip resulting in various errors accross AppVeyor --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 5545fa2b29..06332eb0fe 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,8 +19,7 @@ init: - ps: mkdir C:\tmp install: - 'powershell ./appveyor/install.ps1' - - "python -m pip install tox" - - 'python -m pip install wheel' + - 'python -m pip install -U setuptools pip tox wheel virtualenv' - 'python -m pip --version' - 'python -m tox --version' From 50a670b1eee2a28c771daceeb169a0db4805ddae Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 20 May 2019 09:05:01 +0200 Subject: [PATCH 0106/5147] Expect only certain errors from self regression tests These changes should fix a couple of failing tests when running the suite with pytest instead of tox. The reason for those failures was that pylint was reusing the config file from the root directory and some of its messages are disabled there. Close #2819 --- pylint/test/test_self.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pylint/test/test_self.py b/pylint/test/test_self.py index 1d90845dd7..b5000f98e2 100644 --- a/pylint/test/test_self.py +++ b/pylint/test/test_self.py @@ -581,7 +581,6 @@ def test_parseable_file_path(self): def test_stdin(self, input_path, module, expected_path): expected_output = ( "************* Module {module}\n" - "{path}:1:0: C0111: Missing module docstring (missing-docstring)\n" "{path}:1:0: W0611: Unused import os (unused-import)\n\n" ).format(path=expected_path, module=module) @@ -589,7 +588,8 @@ def test_stdin(self, input_path, module, expected_path): "pylint.lint._read_stdin", return_value="import os\n" ) as mock_stdin: self._test_output( - ["--from-stdin", input_path], expected_output=expected_output + ["--from-stdin", input_path, "--disable=all", "--enable=unused-import"], + expected_output=expected_output, ) assert mock_stdin.call_count == 1 @@ -629,19 +629,27 @@ def foobar(arg): os.chdir(str(tmpdir)) expected = ( "************* Module a.b\n" - "a/b.py:1:0: C0111: Missing module docstring (missing-docstring)\n" "a/b.py:3:0: E0401: Unable to import 'a.d' (import-error)\n\n" ) if write_bpy_to_disk: # --from-stdin is not used here - self._test_output(["a/b.py"], expected_output=expected) + self._test_output( + ["a/b.py", "--disable=all", "--enable=import-error"], + expected_output=expected, + ) # this code needs to work w/ and w/o a file named a/b.py on the # harddisk. with mock.patch("pylint.lint._read_stdin", return_value=b_code): self._test_output( - ["--from-stdin", join("a", "b.py")], expected_output=expected + [ + "--from-stdin", + join("a", "b.py"), + "--disable=all", + "--enable=import-error", + ], + expected_output=expected, ) finally: From 07d5e37b9d2bded9eb98e47dacd1feff6af0188a Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 20 May 2019 09:36:51 +0200 Subject: [PATCH 0107/5147] Support fully qualified typing imports for type annotations. Close #2915 --- ChangeLog | 4 + pylint/checkers/variables.py | 97 ++++++++++++++++--- .../test/functional/unused_typing_imports.py | 19 +++- .../test/functional/unused_typing_imports.rc | 1 - 4 files changed, 108 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index 05c68c6a40..c5ad2ecfec 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Support fully qualified typing imports for type annotations. + + Close #2915 + * Exclude ``__dict__`` from ``attribute-defined-outside-init`` * Fix pointer on spelling check when the error are more than one time in the same line. diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 345aa76dee..26a7966ef4 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -62,6 +62,63 @@ 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): @@ -771,6 +828,11 @@ def _check_imports(self, not_consumed): # 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: @@ -1647,18 +1709,31 @@ def visit_assign(self, node): return def _store_type_annotation_node(self, type_annotation): - - if isinstance(type_annotation, astroid.Name): + """Given a type annotation, store all the name nodes it refers to""" + if ( + isinstance(type_annotation, astroid.Name) + and type_annotation.name in TYPING_NAMES + ): self._type_annotation_names.append(type_annotation.name) - else: - self._type_annotation_names.extend( - list( - ( - annotation.name - for annotation in type_annotation.nodes_of_class(astroid.Name) - ) - ) - ) + return + + if not isinstance(type_annotation, astroid.Subscript): + return + + # Check if it is namespaced by typing or not. + 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) + if annotation.name in TYPING_NAMES + ) def _store_type_annotation_names(self, node): type_annotation = node.type_annotation diff --git a/pylint/test/functional/unused_typing_imports.py b/pylint/test/functional/unused_typing_imports.py index e2fd4ae8a9..6edca71538 100644 --- a/pylint/test/functional/unused_typing_imports.py +++ b/pylint/test/functional/unused_typing_imports.py @@ -6,7 +6,18 @@ """ import re -from typing import Optional, Callable, Iterable, Any, List, Tuple, Set, NamedTuple, Pattern +import typing +from typing import ( + Any, + Callable, + Iterable, + List, + NamedTuple, + Optional, + Pattern, + Set, + Tuple, +) def func1(arg: Optional[Callable]=None): @@ -37,3 +48,9 @@ def __exit__(self, *_args): def func_test_type_comment(param): # type: (NamedTuple) -> Tuple[NamedTuple, Pattern] return param, re.compile('good') + + +def typing_fully_qualified(): + variable = None # type: typing.Optional[str] + other_variable: 'typing.Optional[str]' = None + return variable, other_variable diff --git a/pylint/test/functional/unused_typing_imports.rc b/pylint/test/functional/unused_typing_imports.rc index dca49456e9..0ba2b6333d 100644 --- a/pylint/test/functional/unused_typing_imports.rc +++ b/pylint/test/functional/unused_typing_imports.rc @@ -1,3 +1,2 @@ [testoptions] min_pyver=3.6 -max_pyver=3.7 \ No newline at end of file From 2c403bbf00a52a7821a7e19cfae1f9c4f092d7da Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 20 May 2019 09:39:22 +0200 Subject: [PATCH 0108/5147] Correct infered to inferred --- pylint/checkers/base.py | 22 +++++++++--------- pylint/checkers/classes.py | 24 ++++++++++---------- pylint/checkers/exceptions.py | 10 ++++----- pylint/checkers/newstyle.py | 2 +- pylint/checkers/stdlib.py | 6 ++--- pylint/checkers/strings.py | 2 +- pylint/checkers/typecheck.py | 36 +++++++++++++++--------------- pylint/checkers/utils.py | 22 +++++++++--------- pylint/checkers/variables.py | 42 +++++++++++++++++------------------ 9 files changed, 83 insertions(+), 83 deletions(-) diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 64ca43f6fb..92cd6dfc7a 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -327,8 +327,8 @@ def _determine_function_name_type(node, config=None): isinstance(decorator, astroid.Attribute) and decorator.attrname in property_names ): - infered = utils.safe_infer(decorator) - if infered and infered.qname() in property_classes: + inferred = utils.safe_infer(decorator) + if inferred and inferred.qname() in property_classes: return "attr" # If the function is decorated using the prop_method.{setter,getter} # form, treat it like an attribute as well. @@ -759,12 +759,12 @@ def visit_call(self, node): except astroid.InferenceError: return - def _check_inferred_class_is_abstract(self, infered, node): - if not isinstance(infered, astroid.ClassDef): + 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 infered: + 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 @@ -772,20 +772,20 @@ def _check_inferred_class_is_abstract(self, infered, node): return # __init__ was called - abstract_methods = _has_abstract_methods(infered) + abstract_methods = _has_abstract_methods(inferred) if not abstract_methods: return - metaclass = infered.metaclass() + metaclass = inferred.metaclass() if metaclass is None: # Python 3.4 has `abc.ABC`, which won't be detected # by ClassNode.metaclass() - for ancestor in infered.ancestors(): + for ancestor in inferred.ancestors(): if ancestor.qname() == "abc.ABC": self.add_message( - "abstract-class-instantiated", args=(infered.name,), node=node + "abstract-class-instantiated", args=(inferred.name,), node=node ) break @@ -793,7 +793,7 @@ def _check_inferred_class_is_abstract(self, infered, node): if metaclass.qname() in ABC_METACLASSES: self.add_message( - "abstract-class-instantiated", args=(infered.name,), node=node + "abstract-class-instantiated", args=(inferred.name,), node=node ) def _check_yield_outside_func(self, node): @@ -1404,7 +1404,7 @@ def _check_reversed(self, node): if argument is astroid.Uninferable: return if argument is None: - # Nothing was infered. + # Nothing was inferred. # Try to see if we have iter(). if isinstance(node.args[0], astroid.Call): try: diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 604b8ef452..ad54bcb484 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -346,10 +346,10 @@ def _called_in_methods(func, klass, methods): return False for method in methods: try: - infered = klass.getattr(method) + inferred = klass.getattr(method) except astroid.NotFoundError: continue - for infer_method in infered: + for infer_method in inferred: for call in infer_method.nodes_of_class(astroid.Call): try: bound = next(call.func.infer()) @@ -385,14 +385,14 @@ def _is_attribute_property(name, klass): if attr is astroid.Uninferable: continue try: - infered = next(attr.infer()) + inferred = next(attr.infer()) except astroid.InferenceError: continue - if isinstance(infered, astroid.FunctionDef) and decorated_with_property( - infered + if isinstance(inferred, astroid.FunctionDef) and decorated_with_property( + inferred ): return True - if infered.pytype() == property_name: + if inferred.pytype() == property_name: return True return False @@ -410,7 +410,7 @@ 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 infered value. + one node has been inferred). Otherwise returns inferred value. """ try: inferit = node.infer_call_result(caller, context=context) @@ -418,7 +418,7 @@ def _safe_infer_call_result(node, caller, context=None): except astroid.InferenceError: return None # inference failed except StopIteration: - return None # no values infered + return None # no values inferred try: next(inferit) return None # there is ambiguity on the inferred node @@ -1508,7 +1508,7 @@ def _check_init(self, node): for klass in expr.expr.infer(): if klass is astroid.Uninferable: continue - # The infered klass can be super(), which was + # The inferred klass can be super(), which was # assigned to a variable and the `__init__` # was called later. # @@ -1730,9 +1730,9 @@ def _is_iterator(node): return False def _check_iter(self, node): - infered = _safe_infer_call_result(node, node) - if infered is not None: - if not self._is_iterator(infered): + 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): diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index 85c525e879..0b68278278 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -47,7 +47,7 @@ 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', 'infered node'). + ('original node', 'inferred node'). """ if isinstance(stmt, (astroid.List, astroid.Tuple)): for elt in stmt.elts: @@ -55,10 +55,10 @@ def _annotated_unpack_infer(stmt, context=None): if inferred and inferred is not astroid.Uninferable: yield elt, inferred return - for infered in stmt.infer(context): - if infered is astroid.Uninferable: + for inferred in stmt.infer(context): + if inferred is astroid.Uninferable: continue - yield stmt, infered + yield stmt, inferred def _is_raising(body: typing.List) -> bool: @@ -371,7 +371,7 @@ def _check_catching_non_exception(self, handler, exc, part): return if not isinstance(exc, astroid.ClassDef): - # Don't emit the warning if the infered stmt + # 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: diff --git a/pylint/checkers/newstyle.py b/pylint/checkers/newstyle.py index 97e2fcb655..bc4947c6ec 100644 --- a/pylint/checkers/newstyle.py +++ b/pylint/checkers/newstyle.py @@ -124,7 +124,7 @@ def visit_functiondef(self, node): if klass is not supcls: name = None - # if supcls is not Uninferable, then supcls was infered + # if supcls is not Uninferable, then supcls was inferred # and use its name. Otherwise, try to look # for call.args[0].name if supcls: diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index c57f90e577..35f5b4e0ba 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -374,14 +374,14 @@ def _check_redundant_assert(self, node, infer): ) def _check_datetime(self, node): - """ Check that a datetime was infered. + """ Check that a datetime was inferred. If so, emit boolean-datetime warning. """ try: - infered = next(node.infer()) + inferred = next(node.infer()) except astroid.InferenceError: return - if isinstance(infered, Instance) and infered.qname() == "datetime.time": + if isinstance(inferred, Instance) and inferred.qname() == "datetime.time": self.add_message("boolean-datetime", node=node) def _check_open_mode(self, node): diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py index 9b22e5244c..fd92689f6e 100644 --- a/pylint/checkers/strings.py +++ b/pylint/checkers/strings.py @@ -457,7 +457,7 @@ def _check_new_format_specifiers(self, node, fields, named): """ for key, specifiers in fields: # Obtain the argument. If it can't be obtained - # or infered, skip this check. + # 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 diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 1b96d5d266..333f54ace3 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -905,7 +905,7 @@ def visit_attribute(self, node): break else: # we have not found any node with the attributes, display the - # message for infered nodes + # message for inferred nodes done = set() for owner, name in missingattr: if isinstance(owner, astroid.Instance): @@ -1373,15 +1373,15 @@ def visit_slice(self, node): def visit_with(self, node): for ctx_mgr, _ in node.items: context = astroid.context.InferenceContext() - infered = safe_infer(ctx_mgr, context=context) - if infered is None or infered is astroid.Uninferable: + inferred = safe_infer(ctx_mgr, context=context) + if inferred is None or inferred is astroid.Uninferable: continue - if isinstance(infered, bases.Generator): + if isinstance(inferred, bases.Generator): # Check if we are dealing with a function decorated # with contextlib.contextmanager. if decorated_with( - infered.parent, self.config.contextmanager_decorators + inferred.parent, self.config.contextmanager_decorators ): continue # If the parent of the generator is not the context manager itself, @@ -1410,25 +1410,25 @@ def visit_with(self, node): break else: self.add_message( - "not-context-manager", node=node, args=(infered.name,) + "not-context-manager", node=node, args=(inferred.name,) ) else: try: - infered.getattr("__enter__") - infered.getattr("__exit__") + inferred.getattr("__enter__") + inferred.getattr("__exit__") except exceptions.NotFoundError: - if isinstance(infered, astroid.Instance): + if isinstance(inferred, astroid.Instance): # If we do not know the bases of this class, # just skip it. - if not has_known_bases(infered): + if not has_known_bases(inferred): continue # Just ignore mixin classes. if self.config.ignore_mixin_members: - if infered.name[-5:].lower() == "mixin": + if inferred.name[-5:].lower() == "mixin": continue self.add_message( - "not-context-manager", node=node, args=(infered.name,) + "not-context-manager", node=node, args=(inferred.name,) ) @check_messages("invalid-unary-operand-type") @@ -1464,10 +1464,10 @@ def _check_membership_test(self, node): return if is_comprehension(node): return - infered = safe_infer(node) - if infered is None or infered is astroid.Uninferable: + inferred = safe_infer(node) + if inferred is None or inferred is astroid.Uninferable: return - if not supports_membership_test(infered): + if not supports_membership_test(inferred): self.add_message( "unsupported-membership-test", args=node.as_string(), node=node ) @@ -1615,10 +1615,10 @@ def _check_mapping(self, node): return if isinstance(node, astroid.DictComp): return - infered = safe_infer(node) - if infered is None or infered is astroid.Uninferable: + inferred = safe_infer(node) + if inferred is None or inferred is astroid.Uninferable: return - if not is_mapping(infered): + if not is_mapping(inferred): self.add_message("not-a-mapping", args=node.as_string(), node=node) @check_messages("not-an-iterable") diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 352608f37f..2bb2d35852 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -722,11 +722,11 @@ def decorated_with_property(node: astroid.FunctionDef) -> bool: def _is_property_decorator(decorator: astroid.Name) -> bool: - for infered in decorator.infer(): - if isinstance(infered, astroid.ClassDef): - if infered.root().name == BUILTINS_NAME and infered.name == "property": + 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 infered.ancestors(): + for ancestor in inferred.ancestors(): if ( ancestor.name == "property" and ancestor.root().name == BUILTINS_NAME @@ -778,10 +778,10 @@ def unimplemented_abstract_methods( return {} for ancestor in mro: for obj in ancestor.values(): - infered = obj + inferred = obj if isinstance(obj, astroid.AssignName): - infered = safe_infer(obj) - if not infered: + 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 @@ -789,10 +789,10 @@ def unimplemented_abstract_methods( if obj.name in visited: del visited[obj.name] continue - if not isinstance(infered, astroid.FunctionDef): + if not isinstance(inferred, astroid.FunctionDef): if obj.name in visited: del visited[obj.name] - if isinstance(infered, astroid.FunctionDef): + 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 @@ -801,9 +801,9 @@ def unimplemented_abstract_methods( # class A: # def keys(self): pass # __iter__ = keys - abstract = is_abstract_cb(infered) + abstract = is_abstract_cb(inferred) if abstract: - visited[obj.name] = infered + visited[obj.name] = inferred elif not abstract and obj.name in visited: del visited[obj.name] return visited diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 26a7966ef4..bc967b3604 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -159,19 +159,19 @@ def overridden_method(klass, name): return None -def _get_unpacking_extra_info(node, infered): +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 = "" - infered_module = infered.root().name - if node.root().name == infered_module: - if node.lineno == infered.lineno: - more = " %s" % infered.as_string() - elif infered.lineno: - more = " defined at line %s" % infered.lineno - elif infered.lineno: - more = " defined at line %s of %s" % (infered.lineno, infered_module) + 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 @@ -1702,9 +1702,9 @@ def visit_assign(self, node): targets = node.targets[0].itered() try: - infered = utils.safe_infer(node.value) - if infered is not None: - self._check_unpacking(infered, node, targets) + inferred = utils.safe_infer(node.value) + if inferred is not None: + self._check_unpacking(inferred, node, targets) except astroid.InferenceError: return @@ -1778,7 +1778,7 @@ def _check_self_cls_assign(self, node): if self_cls_name in target_assign_names: self.add_message("self-cls-assignment", node=node, args=(self_cls_name)) - def _check_unpacking(self, infered, node, targets): + def _check_unpacking(self, inferred, node, targets): """ Check for unbalanced tuple unpacking and unpacking non sequences. """ @@ -1786,18 +1786,18 @@ def _check_unpacking(self, infered, node, targets): return if utils.is_comprehension(node): return - if infered is astroid.Uninferable: + if inferred is astroid.Uninferable: return if ( - isinstance(infered.parent, astroid.Arguments) + isinstance(inferred.parent, astroid.Arguments) and isinstance(node.value, astroid.Name) - and node.value.name == infered.parent.vararg + and node.value.name == inferred.parent.vararg ): # Variable-length argument, we can't determine the length. return - if isinstance(infered, (astroid.Tuple, astroid.List)): + if isinstance(inferred, (astroid.Tuple, astroid.List)): # attempt to check unpacking is properly balanced - values = infered.itered() + values = inferred.itered() if len(targets) != len(values): # Check if we have starred nodes. if any(isinstance(target, astroid.Starred) for target in targets): @@ -1806,18 +1806,18 @@ def _check_unpacking(self, infered, node, targets): "unbalanced-tuple-unpacking", node=node, args=( - _get_unpacking_extra_info(node, infered), + _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(infered): + if not utils.is_iterable(inferred): self.add_message( "unpacking-non-sequence", node=node, - args=(_get_unpacking_extra_info(node, infered),), + args=(_get_unpacking_extra_info(node, inferred),), ) def _check_module_attrs(self, node, module, module_names): From 44248e9f67cd066111af139113670f5c315de890 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 20 May 2019 09:48:48 +0200 Subject: [PATCH 0109/5147] Syntax errors report the column number. Close #2914 --- ChangeLog | 4 ++++ pylint/lint.py | 8 +++++--- pylint/test/test_self.py | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index c5ad2ecfec..b98f0e59f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Syntax errors report the column number. + + Close #2914 + * Support fully qualified typing imports for type annotations. Close #2915 diff --git a/pylint/lint.py b/pylint/lint.py index 809e2294bd..f1ebd1a1ef 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -66,6 +66,7 @@ import os import sys import tokenize +import traceback import warnings from io import TextIOWrapper @@ -1176,13 +1177,14 @@ def get_ast(self, filepath, modname): except astroid.AstroidSyntaxError as ex: # pylint: disable=no-member self.add_message( - "syntax-error", line=getattr(ex.error, "lineno", 0), args=str(ex.error) + "syntax-error", + line=getattr(ex.error, "lineno", 0), + col_offset=getattr(ex.error, "offset", None), + args=str(ex.error), ) except astroid.AstroidBuildingException as ex: self.add_message("parse-error", args=ex) except Exception as ex: - import traceback - traceback.print_exc() self.add_message("astroid-error", args=(ex.__class__, ex)) diff --git a/pylint/test/test_self.py b/pylint/test/test_self.py index b5000f98e2..8fe3efe201 100644 --- a/pylint/test/test_self.py +++ b/pylint/test/test_self.py @@ -401,7 +401,7 @@ def test_json_report_when_file_has_syntax_error(self): assert isinstance(output[0], dict) expected = { "obj": "", - "column": 0, + "column": 15, "line": 1, "type": "error", "symbol": "syntax-error", From ace1877788a574362acda463d519d56a728620c5 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 20 May 2019 10:36:07 +0200 Subject: [PATCH 0110/5147] Use the right column number for syntax error on PyPy --- pylint/test/test_self.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pylint/test/test_self.py b/pylint/test/test_self.py index 8fe3efe201..f90a893404 100644 --- a/pylint/test/test_self.py +++ b/pylint/test/test_self.py @@ -23,10 +23,9 @@ import configparser import contextlib import json -import os +import platform import re import subprocess -import sys import tempfile import textwrap from io import StringIO @@ -401,7 +400,7 @@ def test_json_report_when_file_has_syntax_error(self): assert isinstance(output[0], dict) expected = { "obj": "", - "column": 15, + "column": 8 if platform.python_implementation() == "PyPy" else 15, "line": 1, "type": "error", "symbol": "syntax-error", From b38a7c80211123bcc732f97da32b77002f2bde96 Mon Sep 17 00:00:00 2001 From: Ashley Whetter Date: Sat, 18 May 2019 17:24:08 -0700 Subject: [PATCH 0111/5147] Fixed false positive for `undefined-loop-variable` Closes #202 --- ChangeLog | 4 ++++ pylint/checkers/variables.py | 10 ++++++++++ pylint/test/functional/nested_func_defined_in_loop.py | 11 +++++++++++ 3 files changed, 25 insertions(+) create mode 100644 pylint/test/functional/nested_func_defined_in_loop.py diff --git a/ChangeLog b/ChangeLog index b98f0e59f9..5d42d4e9d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -117,6 +117,10 @@ Release date: TBA Close #2877 +* Fixed false `undefined-loop-variable` for a function defined in the loop, + that uses the variable defined in that loop. + + Close #202 What's New in Pylint 2.3.0? =========================== diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index bc967b3604..a64b3ceab3 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1186,6 +1186,16 @@ def _loopvar_name(self, node, name): 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 diff --git a/pylint/test/functional/nested_func_defined_in_loop.py b/pylint/test/functional/nested_func_defined_in_loop.py new file mode 100644 index 0000000000..af34790748 --- /dev/null +++ b/pylint/test/functional/nested_func_defined_in_loop.py @@ -0,0 +1,11 @@ +# pylint: disable=W0640 +"""Check a nested function defined in a loop.""" + +def example(args): + """The check""" + for i in args: + def nested(): + print(i) + nested() + for i in args: + print(i) From 5d0c0fa73bfc1c95f59b8d6e746117777681b260 Mon Sep 17 00:00:00 2001 From: Ashley Whetter Date: Mon, 20 May 2019 23:49:29 -0700 Subject: [PATCH 0112/5147] Fixed pragmas on their own line after a backlash being ignored (#2923) Closes #199 --- ChangeLog | 6 ++++++ pylint/lint.py | 15 +++++++++++++++ pylint/test/functional/pragma_after_backslash.py | 10 ++++++++++ 3 files changed, 31 insertions(+) create mode 100644 pylint/test/functional/pragma_after_backslash.py diff --git a/ChangeLog b/ChangeLog index 5d42d4e9d7..1f9ab64251 100644 --- a/ChangeLog +++ b/ChangeLog @@ -117,11 +117,17 @@ Release date: TBA Close #2877 +* Fixed a pragma comment on its own physical line being ignored when part + of a logical line with the previous physical line. + + Close #199 + * Fixed false `undefined-loop-variable` for a function defined in the loop, that uses the variable defined in that loop. Close #202 + What's New in Pylint 2.3.0? =========================== diff --git a/pylint/lint.py b/pylint/lint.py index f1ebd1a1ef..1718b03a5c 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -833,7 +833,18 @@ def process_tokens(self, tokens): level options """ control_pragmas = {"disable", "enable"} + prev_line = None + saw_newline = True + seen_newline = True for (tok_type, content, start, _, _) in tokens: + if prev_line and prev_line != start[0]: + saw_newline = seen_newline + seen_newline = False + + prev_line = start[0] + if tok_type in (tokenize.NL, tokenize.NEWLINE): + seen_newline = True + if tok_type != tokenize.COMMENT: continue match = OPTION_RGX.search(content) @@ -888,6 +899,10 @@ def process_tokens(self, tokens): self.add_message("file-ignored", line=start[0]) self._ignore_file = True return + # If we did not see a newline between the previous line and now, + # we saw a backslash so treat the two lines as one. + if not saw_newline: + meth(msgid, "module", start[0] - 1) meth(msgid, "module", start[0]) except exceptions.UnknownMessageError: self.add_message("bad-option-value", args=msgid, line=start[0]) diff --git a/pylint/test/functional/pragma_after_backslash.py b/pylint/test/functional/pragma_after_backslash.py new file mode 100644 index 0000000000..c506f6c9da --- /dev/null +++ b/pylint/test/functional/pragma_after_backslash.py @@ -0,0 +1,10 @@ +"""Test that a pragma has an effect when separated by a backslash.""" +# pylint: disable=too-few-public-methods + +class Foo: + """block-disable test""" + + def meth3(self): + """test one line disabling""" + print(self.bla) \ + # pylint: disable=E1101 From 47d97c0bf2e7409fbc75645051ad20db2c298cb0 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Tue, 21 May 2019 09:16:32 +0200 Subject: [PATCH 0113/5147] Add a function to figure out if a node is a property setter and use it accordingly --- pylint/checkers/base.py | 24 ++++++++---------------- pylint/checkers/classes.py | 10 +++------- pylint/checkers/utils.py | 22 +++++++++++++++++++++- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 92cd6dfc7a..e9aecb287d 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -52,6 +52,7 @@ import pylint.utils as lint_utils from pylint import checkers, exceptions, interfaces from pylint.checkers import utils +from pylint.checkers.utils import is_property_deleter, is_property_setter from pylint.reporters.ureports import nodes as reporter_nodes @@ -316,6 +317,12 @@ def _determine_function_name_type(node, config=None): property_classes, property_names = _get_properties(config) if not node.is_method(): return "function" + + if is_property_setter(node) or is_property_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: @@ -330,13 +337,6 @@ def _determine_function_name_type(node, config=None): inferred = utils.safe_infer(decorator) if inferred and inferred.qname() in property_classes: return "attr" - # If the function is decorated using the prop_method.{setter,getter} - # form, treat it like an attribute as well. - elif isinstance(decorator, astroid.Attribute) and decorator.attrname in ( - "setter", - "deleter", - ): - return "attr" return "method" @@ -1915,19 +1915,11 @@ def visit_classdef(self, node): if self.config.no_docstring_rgx.match(node.name) is None: self._check_docstring("class", node) - @staticmethod - def _is_setter_or_deleter(node): - names = {"setter", "deleter"} - for decorator in node.decorators.nodes: - if isinstance(decorator, astroid.Attribute) and decorator.attrname in names: - return True - return False - @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 node.decorators and self._is_setter_or_deleter(node): + if is_property_setter(node) or is_property_deleter(node): return if isinstance(node.parent.frame(), astroid.ClassDef): diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index ad54bcb484..df39331d3a 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -55,6 +55,7 @@ is_builtin_object, is_comprehension, is_iterable, + is_property_setter, node_frame_class, overrides_a_method, safe_infer, @@ -1564,13 +1565,8 @@ def _check_signature(self, method1, refmethod, class_type, cls): return # Ignore setters, they have an implicit extra argument, # which shouldn't be taken in consideration. - if method1.decorators: - for decorator in method1.decorators.nodes: - if ( - isinstance(decorator, astroid.Attribute) - and decorator.attrname == "setter" - ): - return + if is_property_setter(method1): + return if _different_parameters( refmethod, method1, dummy_parameter_regex=self._dummy_rgx diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 2bb2d35852..9c4e718642 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -707,7 +707,7 @@ def stringify_error(error): def decorated_with_property(node: astroid.FunctionDef) -> bool: - """ Detect if the given function node is decorated with a property. """ + """Detect if the given function node is decorated with a property. """ if not node.decorators: return False for decorator in node.decorators.nodes: @@ -721,6 +721,26 @@ def decorated_with_property(node: astroid.FunctionDef) -> bool: return False +def _is_property_kind(node, kind): + 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 == kind: + 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_deleter(node: astroid.FunctionDef) -> bool: + """Check if the given node is a property deleter""" + return _is_property_kind(node, "deleter") + + def _is_property_decorator(decorator: astroid.Name) -> bool: for inferred in decorator.infer(): if isinstance(inferred, astroid.ClassDef): From f9e99fd5f3acaf2d836c7a3456f7538611cd4167 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Tue, 21 May 2019 09:22:23 +0200 Subject: [PATCH 0114/5147] Don't emit ``attribute-defined-outside-init`` for variables defined in setters. Close #409 --- ChangeLog | 4 ++++ pylint/checkers/classes.py | 19 +++++++++++++------ .../attribute_defined_outside_init.py | 13 +++++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1f9ab64251..b00e0ca866 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Don't emit ``attribute-defined-outside-init`` for variables defined in setters. + + Close #409 + * Syntax errors report the column number. Close #2914 diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index df39331d3a..1313f48306 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -811,7 +811,11 @@ def leave_classdef(self, cnode): defining_methods = self.config.defining_attr_methods current_module = cnode.root() for attr, nodes in cnode.instance_attrs.items(): - # skip nodes which are not in the current module and it may screw up + # 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 @@ -821,11 +825,14 @@ def leave_classdef(self, cnode): ] if not nodes: continue # error detected by typechecking - # check if any method attr is defined in is a defining method - if any(node.frame().name in defining_methods for node in nodes): - continue - # Exclude `__dict__` as it is already defined. - if attr == "__dict__": + + # 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__ diff --git a/pylint/test/functional/attribute_defined_outside_init.py b/pylint/test/functional/attribute_defined_outside_init.py index ab5cf5f192..ff31e955db 100644 --- a/pylint/test/functional/attribute_defined_outside_init.py +++ b/pylint/test/functional/attribute_defined_outside_init.py @@ -65,3 +65,16 @@ def test_mixin(self): class F: def func(self): self.__dict__ = {'foo': 'bar'} + + +class Mine: + def __init__(self, param): + self.prop = param + + @property + def prop(self): + return self.__prop + + @prop.setter + def prop(self, value): + self.__prop = value From 6b0003da63d30d0f345f24380c340b064713597f Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 22 May 2019 08:56:46 +0200 Subject: [PATCH 0115/5147] Fix a bunch of linting errors from the codebase --- pylint/__pkginfo__.py | 2 +- pylint/checkers/classes.py | 6 +-- pylint/checkers/exceptions.py | 1 - pylint/checkers/format.py | 8 ++-- pylint/checkers/misc.py | 4 +- pylint/checkers/similar.py | 20 +++++---- pylint/checkers/spelling.py | 6 +-- pylint/checkers/strings.py | 54 +++++++++++------------- pylint/checkers/typecheck.py | 11 +---- pylint/checkers/utils.py | 11 ++--- pylint/checkers/variables.py | 8 +--- pylint/config.py | 10 ----- pylint/extensions/mccabe.py | 12 +++--- pylint/lint.py | 9 +--- pylint/message/message_handler_mix_in.py | 4 +- pylint/pyreverse/inspector.py | 4 -- pylint/pyreverse/main.py | 5 --- pylint/pyreverse/utils.py | 5 ++- pylint/reporters/json_reporter.py | 2 +- pylint/reporters/ureports/text_writer.py | 2 +- pylint/utils/ast_walker.py | 9 ++-- pylint/utils/file_state.py | 8 ++-- pylint/utils/utils.py | 4 +- pylintrc | 3 +- 24 files changed, 77 insertions(+), 131 deletions(-) diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index 82caac1a4d..994162d058 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -16,7 +16,7 @@ # 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=W0622,C0103 +# pylint: disable=redefined-builtin,invalid-name """pylint packaging information""" from __future__ import absolute_import diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 1313f48306..a62521aba1 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -929,7 +929,7 @@ def visit_functiondef(self, node): # check if the method is hidden by an attribute try: - overridden = klass.instance_attr(node.name)[0] # XXX + overridden = klass.instance_attr(node.name)[0] overridden_frame = overridden.frame() if ( isinstance(overridden_frame, astroid.FunctionDef) @@ -1268,8 +1268,7 @@ def _check_protected_attribute_access(self, node): klass = node_frame_class(node) - # XXX infer to be more safe and less dirty ?? - # in classes, check we are not getting a parent method + # In classes, check we are not getting a parent method # through the class object or through super callee = node.expr.as_string() @@ -1331,7 +1330,6 @@ def visit_name(self, node): def _check_accessed_members(self, node, accessed): """check that accessed members are defined""" - # XXX refactor, probably much simpler now that E0201 is in type checker excs = ("AttributeError", "Exception", "BaseException") for attr, nodes in accessed.items(): try: diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index 0b68278278..a89c5f5c78 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -254,7 +254,6 @@ def visit_tuple(self, tuple_node): isinstance(inferred, astroid.Instance) and inferred.__class__.__name__ != "Instance" ): - # TODO: explain why self.visit_default(tuple_node) else: self.visit(inferred) diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index bc935ddd06..691bfe6f94 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -273,8 +273,8 @@ def _get_indent_length(line): 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 ("", "") - # TODO tabs should not be replaced by some random (8) number of spaces + return "", "" + bar_positions = [_get_indent_length(indent) for indent in bar_positions] bad_position = _get_indent_length(bad_position) delta_message = "" @@ -294,7 +294,7 @@ def _get_indent_hint_line(bar_positions, bad_position): line = [" "] * (markers[-1][0] + 1) for position, marker in markers: line[position] = marker - return ("".join(line), delta_message) + return "".join(line), delta_message class _ContinuedIndent: @@ -1168,7 +1168,7 @@ def visit_default(self, node): if not node.is_statement: return if not node.root().pure_python: - return # XXX block visit of child nodes + return prev_sibl = node.previous_sibling() if prev_sibl is not None: prev_line = prev_sibl.fromlineno diff --git a/pylint/checkers/misc.py b/pylint/checkers/misc.py index d4da9f33bd..12dc908154 100644 --- a/pylint/checkers/misc.py +++ b/pylint/checkers/misc.py @@ -18,8 +18,6 @@ """Check source code is ascii only or has an encoding declaration (PEP 263)""" -# pylint: disable=W0511 - import re import tokenize @@ -118,7 +116,7 @@ def _check_encoding(self, lineno, line, file_encoding): self.add_message( "invalid-encoded-data", line=lineno, args=(file_encoding, ex.args[2]) ) - except LookupError as ex: + except LookupError: if line.startswith("#") and "coding" in line and file_encoding in line: self.add_message( "syntax-error", diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py index 1ac5f6196b..7d51d6e3be 100644 --- a/pylint/checkers/similar.py +++ b/pylint/checkers/similar.py @@ -13,7 +13,7 @@ # 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=W0622 +# pylint: disable=redefined-builtin """a similarities / code duplication command line tool and pylint checker """ @@ -97,11 +97,12 @@ def _display_sims(self, 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)) - # pylint: disable=W0631 - for line in lineset._real_lines[idx : idx + num]: - print(" ", line.rstrip()) + 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( @@ -192,8 +193,6 @@ def stripped_lines(lines, ignore_comments, ignore_docstrings, ignore_imports): if current_line_is_import: line = "" if ignore_comments: - # XXX should use regex in checkers/format to avoid cutting - # at a "#" in a string line = line.split("#", 1)[0].strip() strippedlines.append(line) return strippedlines @@ -380,12 +379,15 @@ def close(self): 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() - # pylint: disable=W0631 - for line in lineset._real_lines[idx : idx + num]: - msg.append(line.rstrip()) + + 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 diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py index f04c0a6c5c..2674078b90 100644 --- a/pylint/checkers/spelling.py +++ b/pylint/checkers/spelling.py @@ -334,10 +334,10 @@ def _check_spelling(self, msgid, line, line_num): del suggestions[self.config.max_spelling_suggestions :] line_segment = line[word_start_at:] - m = re.search(r"(\W|^)(%s)(\W|$)" % word, line_segment) - if m: + match = re.search(r"(\W|^)(%s)(\W|$)" % word, line_segment) + if match: # Start position of second group in regex. - col = m.regs[2][0] + col = match.regs[2][0] else: col = line_segment.index(word) diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py index fd92689f6e..ac7b1b73f0 100644 --- a/pylint/checkers/strings.py +++ b/pylint/checkers/strings.py @@ -227,10 +227,12 @@ def visit_binop(self, node): required_keys, required_num_args, required_key_types, required_arg_types = utils.parse_format_string( format_string ) - except utils.UnsupportedFormatCharacter as e: - c = format_string[e.index] + except utils.UnsupportedFormatCharacter as exc: + formatted = format_string[exc.index] self.add_message( - "bad-format-character", node=node, args=(c, ord(c), e.index) + "bad-format-character", + node=node, + args=(formatted, ord(formatted), exc.index), ) return except utils.IncompleteFormatString: @@ -368,13 +370,11 @@ def _detect_vacuous_formatting(self, node, positional_arguments): ) def _check_new_format(self, node, func): - """ Check the new string formatting. """ - # TODO: skip (for now) format 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. - # For more details, see issue 287. - # + """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: # @@ -593,9 +593,6 @@ class StringConstantChecker(BaseTokenChecker): # Unicode or byte strings. ESCAPE_CHARACTERS = "abfnrtvx\n\r\t\\'\"01234567" - # TODO(mbp): Octal characters are quite an edge case today; people may - # prefer a separate warning where they occur. \0 should be allowed. - # Characters that have a special meaning after a backslash but only in # Unicode strings. UNICODE_ESCAPE_CHARACTERS = "uUN" @@ -673,14 +670,14 @@ def check_for_concatenated_strings(self, iterable_node, iterable_type): def process_string_token(self, token, start_row): quote_char = None index = None - for index, c in enumerate(token): - if c in "'\"": - quote_char = c + 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. + 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] @@ -706,20 +703,15 @@ def process_non_raw_string_token(self, prefix, string_body, start_row): # end-of-line, or one of the letters that introduce a special escape # sequence # - # TODO(mbp): Maybe give a separate warning about the rarely-used - # \a \b \v \f? - # - # TODO(mbp): We could give the column of the problem character, but - # add_message doesn't seem to have a way to pass it through at present. - i = 0 + index = 0 while True: - i = string_body.find("\\", i) - if i == -1: + 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[i + 1] - match = string_body[i : i + 2] + 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 @@ -730,15 +722,19 @@ def process_non_raw_string_token(self, prefix, string_body, start_row): "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,) + "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. - i += 2 + index += 2 def register(linter): diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 333f54ace3..d32f90dcc0 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -132,8 +132,6 @@ def _is_owner_ignored(owner, name, ignored_classes, ignored_modules): @singledispatch def _node_names(node): - # TODO: maybe we need an ABC for checking if an object is a scoped node - # or not? if not hasattr(node, "locals"): return [] return node.locals.keys() @@ -451,7 +449,6 @@ def _determine_callable(callable_obj): # and Function inherits Lambda. parameters = 0 if hasattr(callable_obj, "implicit_parameters"): - # TODO: replace with a Callable check parameters = callable_obj.implicit_parameters() if isinstance(callable_obj, astroid.BoundMethod): # Bound methods have an extra implicit 'self' argument. @@ -881,7 +878,6 @@ def visit_attribute(self, node): missingattr.add((owner, name)) continue except AttributeError: - # XXX method / function continue except exceptions.NotFoundError: # This can't be moved before the actual .getattr call, @@ -995,9 +991,6 @@ def _check_uninferable_call(self, node): # there. If that attribute is a property or a subclass of properties, # then most likely it's not callable. - # TODO: since astroid doesn't understand descriptors very well - # we will not handle them here, right now. - expr = node.func.expr klass = safe_infer(expr) if ( @@ -1179,7 +1172,7 @@ def visit_call(self, node): else: parameters[i][1] = True elif keyword in kwparams: - if kwparams[keyword][1]: # XXX is that even possible? + if kwparams[keyword][1]: # Duplicate definition of function parameter. self.add_message( "redundant-keyword-arg", @@ -1216,7 +1209,6 @@ def visit_call(self, node): display_name = "" else: display_name = repr(name) - # TODO(cpopa): this should be removed after PyCQA/astroid/issues/177 if not has_no_context_positional_variadic: self.add_message( "no-value-for-parameter", @@ -1631,7 +1623,6 @@ def visit_asyncfor(self, node): @check_messages("not-an-iterable") def visit_yieldfrom(self, node): - # TODO: hack which can be removed once we support decorators inference if self._is_asyncio_coroutine(node.value): return self._check_iterable(node.value) diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 9c4e718642..3d860fc47e 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -231,15 +231,14 @@ def get_all_elements( """Recursively returns all atoms in nested lists and tuples.""" if isinstance(node, (astroid.Tuple, astroid.List)): for child in node.elts: - for e in get_all_elements(child): - yield e + yield from get_all_elements(child) else: yield node def clobber_in_except( node: astroid.node_classes.NodeNG -) -> Tuple[bool, Tuple[str, str]]: +) -> Tuple[bool, Optional[Tuple[str, str]]]: """Checks if an assignment node in an except handler clobbers an existing variable. @@ -251,7 +250,7 @@ def clobber_in_except( if isinstance(node, astroid.AssignName): name = node.name if is_builtin(name): - return (True, (name, "builtins")) + return True, (name, "builtins") stmts = node.lookup(name)[1] if stmts and not isinstance( @@ -1027,8 +1026,6 @@ def _supports_protocol( if protocol_callback(value): return True - # TODO: this is not needed in astroid 2.0, where we can - # check the type using a virtual base class instead. if ( isinstance(value, _bases.Proxy) and isinstance(value._proxied, astroid.BaseInstance) @@ -1072,7 +1069,6 @@ def supports_delitem(value: astroid.node_classes.NodeNG) -> bool: return _supports_protocol(value, _supports_delitem_protocol) -# TODO(cpopa): deprecate these or leave them as aliases? @lru_cache(maxsize=1024) def safe_infer( node: astroid.node_classes.NodeNG, context=None @@ -1104,7 +1100,6 @@ def has_known_bases(klass: astroid.ClassDef, context=None) -> bool: pass for base in klass.bases: result = safe_infer(base, context=context) - # TODO: check for A->B->A->B pattern in class structure too? if ( not isinstance(result, astroid.ClassDef) or result is klass diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index a64b3ceab3..c91880c24b 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -214,11 +214,11 @@ class C: ... return False break_scopes = [] - for s in (scope, def_scope): + 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 = s + parent_scope = current_scope while parent_scope: if not isinstance(parent_scope, (astroid.ClassDef, astroid.Module)): break_scopes.append(parent_scope) @@ -252,7 +252,6 @@ def _fix_dot_imports(not_consumed): like 'xml' (when we have both 'xml.etree' and 'xml.sax'), to 'xml.etree' and 'xml.sax' respectively. """ - # TODO: this should be improved in issue astroid #46 names = {} for name, stmts in not_consumed.items(): if any( @@ -984,7 +983,6 @@ def _check_is_unused(self, name, node, stmt, global_names, nonlocal_names): return # Ignore names imported by the global statement. - # FIXME: should only ignore them if it's assigned latter 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): @@ -1855,8 +1853,6 @@ def _check_module_attrs(self, node, module, module_names): except astroid.InferenceError: return None if module_names: - # FIXME: other message if name is not the latest part of - # module_names ? modname = module.name if module else "__dict__" self.add_message( "no-name-in-module", node=node, args=(".".join(module_names), modname) diff --git a/pylint/config.py b/pylint/config.py index 8b0373f612..9ccc1f28cc 100644 --- a/pylint/config.py +++ b/pylint/config.py @@ -49,14 +49,6 @@ from pylint import utils -# TODO(cpopa): this module contains the logic for the -# configuration parser and for the command line parser, -# but it's really coupled to optparse's internals. -# The code was copied almost verbatim from logilab.common, -# in order to not depend on it anymore and it will definitely -# need a cleanup. It could be completely reengineered as well. - - USER_HOME = os.path.expanduser("~") if "PYLINTHOME" in os.environ: PYLINT_HOME = os.environ["PYLINTHOME"] @@ -245,7 +237,6 @@ def _validate(value, optdict, name=""): try: _type = optdict["type"] except KeyError: - # FIXME return value return _call_validator(_type, optdict, name, value) @@ -751,7 +742,6 @@ def load_config_file(self): try: self.global_set_option(option, value) except (KeyError, optparse.OptionError): - # TODO handle here undeclared options appearing in the config file continue def load_configuration(self, **kwargs): diff --git a/pylint/extensions/mccabe.py b/pylint/extensions/mccabe.py index 69bc1775ba..fa9cfd1b0a 100644 --- a/pylint/extensions/mccabe.py +++ b/pylint/extensions/mccabe.py @@ -37,8 +37,8 @@ def dispatch(self, node, *args): klass = node.__class__ meth = self._cache.get(klass) if meth is None: - className = klass.__name__ - meth = getattr(self.visitor, "visit" + className, self.default) + class_name = klass.__name__ + meth = getattr(self.visitor, "visit" + class_name, self.default) self._cache[klass] = meth return meth(node, *args) @@ -116,9 +116,7 @@ def _subgraph(self, node, name, extra_blocks=()): self._append_node(node) self._subgraph_parse(node, node, extra_blocks) - def _subgraph_parse( - self, node, pathnode, extra_blocks - ): # pylint: disable=unused-argument + def _subgraph_parse(self, node, pathnode, extra_blocks): """parse the body and any `else` block of `if` and `for` statements""" loose_ends = [] self.tail = node @@ -137,8 +135,8 @@ def _subgraph_parse( if node: bottom = "%s" % self._bottom_counter self._bottom_counter += 1 - for le in loose_ends: - self.graph.connect(le, bottom) + for end in loose_ends: + self.graph.connect(end, bottom) self.tail = bottom diff --git a/pylint/lint.py b/pylint/lint.py index 1718b03a5c..f599196490 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -101,7 +101,6 @@ def _ast_from_string(data, filepath, modname): def _read_stdin(): # https://mail.python.org/pipermail/python-list/2012-November/634424.html - # FIXME should this try to check the file's declared encoding? sys.stdin = TextIOWrapper(sys.stdin.detach(), encoding="utf-8") return sys.stdin.read() @@ -765,7 +764,6 @@ def register_checker(self, checker): checker.load_defaults() # Register the checker, but disable all of its messages. - # TODO(cpopa): we should have a better API for this. if not getattr(checker, "enabled", True): self.disable(checker.name) @@ -1136,9 +1134,7 @@ def _do_check(self, files_or_modules): ast_node = self.get_ast(filepath, modname) if ast_node is None: continue - # XXX to be correct we need to keep module_msgs_state for every - # analyzed module (the problem stands with localized messages which - # are only detected in the .close step) + self.file_state = FileState(descr["basename"]) self._ignore_file = False # fix the current file (if the source file was not available or @@ -1253,7 +1249,6 @@ def generate_reports(self): if self.file_state.base_name is not None: # load previous results if any previous_stats = config.load_results(self.file_state.base_name) - # XXX code below needs refactoring to be more reporter agnostic self.reporter.on_close(self.stats, previous_stats) if self.config.reports: sect = self.make_reports(self.stats, previous_stats) @@ -1746,7 +1741,7 @@ def cb_full_documentation(self, option, optname, value, parser): self.linter.print_full_documentation() sys.exit(0) - def cb_list_messages(self, option, optname, value, parser): # FIXME + def cb_list_messages(self, option, optname, value, parser): """optik callback for printing available messages""" self.linter.msgs_store.list_messages() sys.exit(0) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 79746f7272..53eb86870a 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -300,9 +300,7 @@ def add_one_message( if line is None and node is not None: line = node.fromlineno if col_offset is None and hasattr(node, "col_offset"): - col_offset = ( - node.col_offset - ) # XXX measured in bytes for utf-8, divide by two for chars? + col_offset = node.col_offset # should this message be displayed if not self.is_message_enabled(msgid, line, confidence): diff --git a/pylint/pyreverse/inspector.py b/pylint/pyreverse/inspector.py index b4df388be4..92418be68f 100644 --- a/pylint/pyreverse/inspector.py +++ b/pylint/pyreverse/inspector.py @@ -40,7 +40,6 @@ def _astroid_wrapper(func, modname): def interfaces(node, herited=True, handler_func=_iface_hdlr): """Return an iterator on interfaces implemented by the given class node.""" - # FIXME: what if __implements__ = (MyIFace, MyParent.__implements__)... try: implements = bases.Instance(node).getattr("__implements__")[0] except exceptions.NotFoundError: @@ -265,8 +264,6 @@ def visit_importfrom(self, node): fullname = "%s.%s" % (basename, name[0]) if fullname.find(".") > -1: try: - # TODO: don't use get_module_part, - # missing package precedence fullname = modutils.get_module_part(fullname, context_file) except ImportError: continue @@ -346,7 +343,6 @@ def project_from_files( ast = func_wrapper(astroid_manager.ast_from_file, fpath) if ast is None: continue - # XXX why is first file defining the project.path ? project.path = project.path or ast.file project.add_module(ast) base_name = ast.name diff --git a/pylint/pyreverse/main.py b/pylint/pyreverse/main.py index 8d30eacbad..98d5ec4371 100644 --- a/pylint/pyreverse/main.py +++ b/pylint/pyreverse/main.py @@ -113,11 +113,6 @@ help="include module name in representation of classes", ), ), - # TODO : generate dependencies like in pylint - # ("package-dependencies", - # dict(short="M", action="store", metavar='', type='int', - # help='show module dependencies beyond modules in \ - # (for the package diagram)')), ( "only-classnames", dict( diff --git a/pylint/pyreverse/utils.py b/pylint/pyreverse/utils.py index b60f336abf..8c52432029 100644 --- a/pylint/pyreverse/utils.py +++ b/pylint/pyreverse/utils.py @@ -203,13 +203,14 @@ class LocalsVisitor(ASTWalker): def __init__(self): ASTWalker.__init__(self, self) - self._visited = {} + self._visited = set() def visit(self, node): """launch the visit starting from the given node""" if node in self._visited: return None - self._visited[node] = 1 # FIXME: use set ? + + self._visited.add(node) methods = self.get_callbacks(node) if methods[0] is not None: methods[0](node) diff --git a/pylint/reporters/json_reporter.py b/pylint/reporters/json_reporter.py index efac6f5c3f..c5523ba95b 100644 --- a/pylint/reporters/json_reporter.py +++ b/pylint/reporters/json_reporter.py @@ -48,7 +48,7 @@ def display_messages(self, layout): """Launch layouts display""" print(json.dumps(self.messages, indent=4), file=self.out) - def display_reports(self, layout): # pylint: disable=arguments-differ + def display_reports(self, layout): """Don't do nothing in this reporter.""" def _display(self, layout): diff --git a/pylint/reporters/ureports/text_writer.py b/pylint/reporters/ureports/text_writer.py index acb6a9ac84..5f05ccbd49 100644 --- a/pylint/reporters/ureports/text_writer.py +++ b/pylint/reporters/ureports/text_writer.py @@ -71,7 +71,7 @@ def default_table(self, layout, table_content, cols_width): format_strings = format_strings.split(" ") table_linesep = "\n+" + "+".join(["-" * w for w in cols_width]) + "+\n" headsep = "\n+" + "+".join(["=" * w for w in cols_width]) + "+\n" - # FIXME: layout.cheaders + self.write(table_linesep) for index, line in enumerate(table_content): self.write("|") diff --git a/pylint/utils/ast_walker.py b/pylint/utils/ast_walker.py index 1350d61f22..2e7a6da972 100644 --- a/pylint/utils/ast_walker.py +++ b/pylint/utils/ast_walker.py @@ -26,7 +26,6 @@ def _is_method_enabled(self, method): def add_checker(self, checker): """walk to the checker's dir and collect visit and leave methods""" - # XXX : should be possible to merge needed_checkers and add_checker vcids = set() lcids = set() visits = self.visit_events @@ -71,10 +70,10 @@ def walk(self, astroid): if astroid.is_statement: self.nbstatements += 1 # generate events for this node on each checker - for cb in visit_events or (): - cb(astroid) + for callback in visit_events or (): + callback(astroid) # recurse on children for child in astroid.get_children(): self.walk(child) - for cb in leave_events or (): - cb(astroid) + for callback in leave_events or (): + callback(astroid) diff --git a/pylint/utils/file_state.py b/pylint/utils/file_state.py index 468445cf38..7878d34142 100644 --- a/pylint/utils/file_state.py +++ b/pylint/utils/file_state.py @@ -45,10 +45,10 @@ def _collect_block_lines(self, msgs_store, node, msg_state): # # 1. def meth8(self): # 2. """test late disabling""" - # 3. # pylint: disable=E1102 - # 4. print self.blip - # 5. # pylint: disable=E1101 - # 6. print self.bla + # 3. pylint: disable=not-callable + # 4. print(self.blip) + # 5. pylint: disable=no-member + # 6. print(self.bla) # # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6 # diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index e78cd98a43..ab28a07b5b 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -127,8 +127,8 @@ def expand_modules(files_or_modules, black_list, black_list_re): if filepath is None: continue except (ImportError, SyntaxError) as ex: - # FIXME p3k : the SyntaxError is a Python bug and should be - # removed as soon as possible http://bugs.python.org/issue10588 + # The SyntaxError is a Python bug and should be + # removed once we move away from imp.find_module: http://bugs.python.org/issue10588 errors.append({"key": "fatal", "mod": modname, "ex": ex}) continue diff --git a/pylintrc b/pylintrc index 112277e0ae..3475fb7673 100644 --- a/pylintrc +++ b/pylintrc @@ -40,7 +40,7 @@ confidence= # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time. See also the "--disable" option for examples. -#enable= +enable=use-symbolic-message-instead,useless-supression,fixme # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this @@ -55,7 +55,6 @@ confidence= disable= attribute-defined-outside-init, duplicate-code, - fixme, invalid-name, missing-docstring, protected-access, From 0a6f65960988ac7c88b166a66e1c548b04d0fbf5 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 22 May 2019 09:41:57 +0200 Subject: [PATCH 0116/5147] Use explicit astroid imports where implicit ones were being used --- pylint/checkers/imports.py | 12 ++++++------ pylint/checkers/utils.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index 20b02aea7c..fcb84c4c89 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -38,6 +38,8 @@ import astroid import isort +from astroid import modutils +from astroid.decorators import cached from pylint.checkers import BaseChecker from pylint.checkers.utils import ( @@ -819,16 +821,14 @@ def _add_imported_module(self, node, importedmodname): base = os.path.splitext(os.path.basename(module_file))[0] try: - importedmodname = astroid.modutils.get_module_part( - importedmodname, module_file - ) + importedmodname = modutils.get_module_part(importedmodname, module_file) except ImportError: pass if context_name == importedmodname: self.add_message("import-self", node=node) - elif not astroid.modutils.is_standard_module(importedmodname): + 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 @@ -938,14 +938,14 @@ def _filter_dependencies_graph(self, internal): graph[importee].add(importer) return graph - @astroid.decorators.cached + @cached def _external_dependencies_info(self): """return cached external dependencies information or build and cache them """ return self._filter_dependencies_graph(internal=False) - @astroid.decorators.cached + @cached def _internal_dependencies_info(self): """return cached internal dependencies information or build and cache them diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 3d860fc47e..2f74028925 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -40,7 +40,7 @@ import astroid from astroid import bases as _bases -from astroid import scoped_nodes +from astroid import helpers, scoped_nodes from astroid.exceptions import _NonDeducibleTypeHierarchy import _string # pylint: disable=wrong-import-position, wrong-import-order @@ -1220,7 +1220,7 @@ def is_subclass_of(child: astroid.ClassDef, parent: astroid.ClassDef) -> bool: for ancestor in child.ancestors(): try: - if astroid.helpers.is_subtype(ancestor, parent): + if helpers.is_subtype(ancestor, parent): return True except _NonDeducibleTypeHierarchy: continue From 3dd27c907b1230f889d0c010de51a99e9291a60f Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 22 May 2019 09:44:49 +0200 Subject: [PATCH 0117/5147] Protect against non-Const nodes in is_complex_format_str --- pylint/checkers/logging.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pylint/checkers/logging.py b/pylint/checkers/logging.py index 7d8dd5b69b..3d979da9b2 100644 --- a/pylint/checkers/logging.py +++ b/pylint/checkers/logging.py @@ -288,11 +288,8 @@ def _check_format_string(self, node, format_arg): # formatting characters - it's used verbatim. Don't check any further. return format_string = node.args[format_arg].value - if not isinstance(format_string, str): - # If the log format is constant non-string (e.g. logging.debug(5)), - # ensure there are no arguments. - required_num_args = 0 - else: + required_num_args = 0 + if isinstance(format_string, str): try: if self._format_style == "old": keyword_args, required_num_args, _, _ = utils.parse_format_string( @@ -339,7 +336,9 @@ def is_complex_format_str(node): bool: True if inferred string uses complex formatting, False otherwise """ inferred = utils.safe_infer(node) - if inferred is None or not isinstance(inferred.value, str): + 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)) From f4fc3a1bbcb58a2f4d110e9bbc76d57f29172ada Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 23 May 2019 08:15:36 +0200 Subject: [PATCH 0118/5147] ``assignment-from-no-return`` not triggered for async methods. Close #2902 --- ChangeLog | 4 ++++ pylint/checkers/typecheck.py | 5 +++++ .../test/functional/assignment_from_no_return_py3.py | 12 ++++++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index b00e0ca866..d1cbe5b38d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* ``assignment-from-no-return`` not triggered for async methods. + + Close #2902 + * Don't emit ``attribute-defined-outside-init`` for variables defined in setters. Close #409 diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index d32f90dcc0..d814918d6b 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -956,6 +956,11 @@ def visit_assign(self, node): and not function_node.decorators ): return + + if isinstance(function_node, astroid.BoundMethod): + # Unwrap to get the actual function object + function_node = function_node._proxied._proxied + if ( function_node.is_generator() or function_node.is_abstract(pass_is_abstract=False) diff --git a/pylint/test/functional/assignment_from_no_return_py3.py b/pylint/test/functional/assignment_from_no_return_py3.py index 2339706ce4..7b1abd59f9 100644 --- a/pylint/test/functional/assignment_from_no_return_py3.py +++ b/pylint/test/functional/assignment_from_no_return_py3.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-docstring +# pylint: disable=missing-docstring,too-few-public-methods import asyncio @@ -16,10 +16,18 @@ async def combining_coroutine1(): await bla2() +class Coro: + async def func(self): + future1 = bla1() + future2 = bla2() + await asyncio.gather(future1, future2) + + async def combining_coroutine2(): future1 = bla1() future2 = bla2() - await asyncio.gather(future1, future2) + future3 = Coro().func() + await asyncio.gather(future1, future2, future3) def do_stuff(): From f90b223a5ea70a0b9a52034280fe22638e6ef14d Mon Sep 17 00:00:00 2001 From: Ashley Whetter Date: Wed, 22 May 2019 23:17:33 -0700 Subject: [PATCH 0119/5147] Can choose decorators that mutate a function's signature (#2926) Close #259 --- ChangeLog | 5 +++++ doc/whatsnew/2.4.rst | 11 +++++++++++ pylint/checkers/typecheck.py | 16 ++++++++++++++++ pylint/test/functional/arguments.py | 20 ++++++++++++++++++++ pylint/test/functional/arguments.rc | 2 ++ 5 files changed, 54 insertions(+) create mode 100644 pylint/test/functional/arguments.rc diff --git a/ChangeLog b/ChangeLog index d1cbe5b38d..f8b931a060 100644 --- a/ChangeLog +++ b/ChangeLog @@ -125,6 +125,11 @@ Release date: TBA Close #2877 +* Can choose to ignore `too-many-function-args`, `unexpected-keyword-arg`, + and `no-value-for-parameter` for functions decorated with certain decorators. + + Close #259 + * Fixed a pragma comment on its own physical line being ignored when part of a logical line with the previous physical line. diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index 42fe36c2c5..fa9cc5d3b4 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -116,3 +116,14 @@ The following does not trigger a ``missing-return-doc`` anymore :: List of strings """ return ["hi", "bye"] #@ + +* Can choose to ignore `too-many-function-args`, `unexpected-keyword-arg`, + and `no-value-for-parameter` for functions decorated with certain decorators. + +For example a test may want to make use of hypothesis. +Adding `hypothesis.extra.numpy.arrays` to `function_mutators` +would mean that `no-value-for-parameter` would not be raised for:: + + @given(img=arrays(dtype=np.float32, shape=(3, 3, 3, 3))) + def test_image(img): + ... diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index d814918d6b..85454c4a05 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -762,6 +762,16 @@ class should be ignored. A mixin class is detected if its name ends with \ "found. The aspect of finding the hint is based on edit distance.", }, ), + ( + "signature-mutators", + { + "default": [], + "type": "csv", + "metavar": "", + "help": "List of decorators that change the signature of " + "a decorated function.", + }, + ), ) @decorators.cachedproperty @@ -1089,6 +1099,12 @@ def visit_call(self, node): # 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()) diff --git a/pylint/test/functional/arguments.py b/pylint/test/functional/arguments.py index ea833ebb7f..d023244d62 100644 --- a/pylint/test/functional/arguments.py +++ b/pylint/test/functional/arguments.py @@ -215,3 +215,23 @@ def some_func(first, second, third): partial(some_func, 1)(1) # [no-value-for-parameter] partial(some_func, 1)(third=1) # [no-value-for-parameter] partial(some_func, 1, 2)(third=1, fourth=4) # [unexpected-keyword-arg] + + +def mutation_decorator(fun): + """Decorator that changes a function's signature.""" + def wrapper(*args, do_something=True, **kwargs): + if do_something: + return fun(*args, **kwargs) + + return None + + return wrapper + + +@mutation_decorator +def mutated_function(arg): + return arg + + +mutated_function(do_something=False) +mutated_function() diff --git a/pylint/test/functional/arguments.rc b/pylint/test/functional/arguments.rc new file mode 100644 index 0000000000..bb6bf0e374 --- /dev/null +++ b/pylint/test/functional/arguments.rc @@ -0,0 +1,2 @@ +[TYPECHECK] +signature-mutators=functional.arguments.mutation_decorator From b657df6644740e77fa167d4f95c76c4c007d2a49 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 23 May 2019 08:20:42 +0200 Subject: [PATCH 0120/5147] Mention the name of the new signature-mutators option --- ChangeLog | 9 ++++++--- doc/whatsnew/2.4.rst | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index f8b931a060..c72c43999d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -125,10 +125,13 @@ Release date: TBA Close #2877 -* Can choose to ignore `too-many-function-args`, `unexpected-keyword-arg`, - and `no-value-for-parameter` for functions decorated with certain decorators. +* ``signature-mutators`` option was added - Close #259 + With this option, users can choose to ignore `too-many-function-args`, `unexpected-keyword-arg`, + and `no-value-for-parameter` for functions decorated with decorators that change + the signature of a decorated function. + + Close #259 * Fixed a pragma comment on its own physical line being ignored when part of a logical line with the previous physical line. diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst index fa9cc5d3b4..1a1ea41da9 100644 --- a/doc/whatsnew/2.4.rst +++ b/doc/whatsnew/2.4.rst @@ -117,11 +117,14 @@ The following does not trigger a ``missing-return-doc`` anymore :: """ return ["hi", "bye"] #@ -* Can choose to ignore `too-many-function-args`, `unexpected-keyword-arg`, - and `no-value-for-parameter` for functions decorated with certain decorators. +* ``signature-mutators`` CLI and config option was added. + +With this option, users can choose to ignore `too-many-function-args`, `unexpected-keyword-arg`, +and `no-value-for-parameter` for functions decorated with decorators that change +the signature of a decorated function. For example a test may want to make use of hypothesis. -Adding `hypothesis.extra.numpy.arrays` to `function_mutators` +Adding `hypothesis.extra.numpy.arrays` to `signature_mutators` would mean that `no-value-for-parameter` would not be raised for:: @given(img=arrays(dtype=np.float32, shape=(3, 3, 3, 3))) From 3bfec9f63b53abd11131c164c4fa941ee1743286 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 23 May 2019 08:53:09 +0200 Subject: [PATCH 0121/5147] ``unused-import`` emitted for the right import names in function scopes. Close #2928 --- ChangeLog | 4 ++++ pylint/checkers/variables.py | 13 ++++++++++--- pylint/test/functional/unused_variable.py | 5 +++++ pylint/test/functional/unused_variable.txt | 1 + 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index c72c43999d..9ad2a79b67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* ``unused-import`` emitted for the right import names in function scopes. + + Close #2928 + * ``assignment-from-no-return`` not triggered for async methods. Close #2902 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index c91880c24b..921b8e749d 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -841,7 +841,6 @@ def _check_imports(self, not_consumed): if not self._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. @@ -1034,10 +1033,18 @@ def _check_is_unused(self, name, node, stmt, global_names, nonlocal_names): 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. - qname, asname = stmt.names[0] - name = asname or qname + 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" diff --git a/pylint/test/functional/unused_variable.py b/pylint/test/functional/unused_variable.py index 4e6d58fe48..e475a90389 100644 --- a/pylint/test/functional/unused_variable.py +++ b/pylint/test/functional/unused_variable.py @@ -53,3 +53,8 @@ def some_other_scope(): def unused_import_from(): from functools import wraps as abc # [unused-import] from collections import namedtuple # [unused-import] + + +def unused_import_in_function(value): + from six import PY2, text_type # [unused-import] + return value.encode("utf-8") if PY2 else value diff --git a/pylint/test/functional/unused_variable.txt b/pylint/test/functional/unused_variable.txt index 231528be39..e9f7ef0220 100644 --- a/pylint/test/functional/unused_variable.txt +++ b/pylint/test/functional/unused_variable.txt @@ -10,3 +10,4 @@ unused-variable:41:locals_example_defined_after:Unused variable 'value' unused-variable:46:locals_does_not_account_for_subscopes:Unused variable 'value' unused-import:54:unused_import_from:Unused wraps imported from functools as abc unused-import:55:unused_import_from:Unused namedtuple imported from collections +unused-import:59:unused_import_in_function:Unused text_type imported from six From a9cb1c740a9f3d570bc90b42d1d3015aee95712c Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 23 May 2019 09:41:10 +0200 Subject: [PATCH 0122/5147] Disable too-many-branches for complex function --- pylint/checkers/variables.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 921b8e749d..4ae13cadf3 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -969,6 +969,7 @@ def _is_name_ignored(self, stmt, name): return regex and regex.match(name) 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 From 1669bc3868b198a1e97fae4fa60e0d6cfaed53d9 Mon Sep 17 00:00:00 2001 From: Ashley Whetter Date: Sun, 26 May 2019 00:29:11 -0700 Subject: [PATCH 0123/5147] Fixed false positives for function stubs (#2927) Close #1581 --- ChangeLog | 4 ++ pylint/checkers/base.py | 13 ++++- pylint/checkers/utils.py | 12 ++++ pylint/checkers/variables.py | 79 +++++++++++++++------------ pylint/test/functional/typing_use.py | 51 +++++++++++++++++ pylint/test/functional/typing_use.rc | 2 + pylint/test/functional/typing_use.txt | 1 + 7 files changed, 127 insertions(+), 35 deletions(-) create mode 100644 pylint/test/functional/typing_use.py create mode 100644 pylint/test/functional/typing_use.rc create mode 100644 pylint/test/functional/typing_use.txt diff --git a/ChangeLog b/ChangeLog index 9ad2a79b67..5afba3ebc6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -147,6 +147,10 @@ Release date: TBA Close #202 +* Fixed `unused-argument` and `function-redefined` getting raised for + functions decorated with `typing.overload`. + + Close #1581 What's New in Pylint 2.3.0? =========================== diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index e9aecb287d..76ef096085 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -836,7 +836,15 @@ def _check_in_loop(self, node, node_name): def _check_redefinition(self, redeftype, node): """check for redefinition of a function / method / class name""" parent_frame = node.parent.frame() - defined_self = parent_frame[node.name] + # Ignore function stubs created for type information + defined_self = next( + ( + local + for local in parent_frame.locals[node.name] + 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 @@ -847,6 +855,9 @@ def _check_redefinition(self, redeftype, node): ): return + if utils.is_overload_stub(node): + return + dummy_variables_rgx = lint_utils.get_global_option( self, "dummy-variables-rgx", default=None ) diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 2f74028925..826181845f 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -1225,3 +1225,15 @@ def is_subclass_of(child: astroid.ClassDef, parent: astroid.ClassDef) -> bool: 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. + """ + return isinstance(node, astroid.FunctionDef) and decorated_with( + node, ["typing.overload"] + ) diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 4ae13cadf3..26cb945552 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -968,6 +968,45 @@ def _is_name_ignored(self, stmt, name): 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 + + self.add_message("unused-argument", args=name, node=stmt, confidence=confidence) + 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. @@ -991,42 +1030,9 @@ def _check_is_unused(self, name, node, stmt, global_names, nonlocal_names): argnames = list( itertools.chain(node.argnames(), [arg.name for arg in node.args.kwonlyargs]) ) - 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 - # Care about functions with unknown argument (builtins) if name in argnames: - 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 - self.add_message( - "unused-argument", args=name, node=stmt, confidence=confidence - ) + self._check_unused_arguments(name, node, stmt, argnames) else: if stmt.parent and isinstance( stmt.parent, (astroid.Assign, astroid.AnnAssign) @@ -1069,6 +1075,11 @@ def _check_is_unused(self, name, node, stmt, global_names, nonlocal_names): 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 leave_functiondef(self, node): diff --git a/pylint/test/functional/typing_use.py b/pylint/test/functional/typing_use.py new file mode 100644 index 0000000000..3e2c48ba0a --- /dev/null +++ b/pylint/test/functional/typing_use.py @@ -0,0 +1,51 @@ +# pylint: disable=missing-docstring + +import typing + + +@typing.overload +def double_with_docstring(arg: str) -> str: + """Return arg, concatenated with itself.""" + + +@typing.overload +def double_with_docstring(arg: int) -> int: + """Return twice arg.""" + + +def double_with_docstring(arg): + """Return 2 * arg.""" + return 2 * arg + + +def double_with_docstring(arg): # [function-redefined] + """Redefined function implementation""" + return 2 * arg + + +@typing.overload +def double_with_ellipsis(arg: str) -> str: + ... + + +@typing.overload +def double_with_ellipsis(arg: int) -> int: + ... + + +def double_with_ellipsis(arg): + return 2 * arg + + +@typing.overload +def double_with_pass(arg: str) -> str: + pass + + +@typing.overload +def double_with_pass(arg: int) -> int: + pass + + +def double_with_pass(arg): + return 2 * arg diff --git a/pylint/test/functional/typing_use.rc b/pylint/test/functional/typing_use.rc new file mode 100644 index 0000000000..0ba2b6333d --- /dev/null +++ b/pylint/test/functional/typing_use.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.6 diff --git a/pylint/test/functional/typing_use.txt b/pylint/test/functional/typing_use.txt new file mode 100644 index 0000000000..1d0233c98b --- /dev/null +++ b/pylint/test/functional/typing_use.txt @@ -0,0 +1 @@ +function-redefined:21:double_with_docstring:"function already defined line 16" From ebd0e206f06b8caa87efdb3ce968da5b7ddac1c0 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Wed, 29 May 2019 12:07:38 +0200 Subject: [PATCH 0124/5147] Replace the modname and distname to the actual name of the package --- pylint/__pkginfo__.py | 2 -- setup.py | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index 994162d058..f7082f097d 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -23,8 +23,6 @@ from os.path import join -modname = distname = "pylint" - # For an official release, use dev_version = None numversion = (2, 4, 0) dev_version = 0 diff --git a/setup.py b/setup.py index 2e76e4878b..bcf38bf617 100644 --- a/setup.py +++ b/setup.py @@ -50,8 +50,6 @@ __pkginfo__ = {} with open(os.path.join(base_dir, "pylint", "__pkginfo__.py")) as f: exec(f.read(), __pkginfo__) -modname = __pkginfo__["modname"] -distname = __pkginfo__.get("distname", modname) scripts = __pkginfo__.get("scripts", []) data_files = __pkginfo__.get("data_files", None) include_dirs = __pkginfo__.get("include_dirs", []) @@ -144,7 +142,7 @@ def install(**kwargs): if USE_SETUPTOOLS: if "--force-manifest" in sys.argv: sys.argv.remove("--force-manifest") - packages = [modname] + get_packages(join(base_dir, "pylint"), modname) + packages = ["pylint"] + get_packages(join(base_dir, "pylint"), "pylint") if USE_SETUPTOOLS: if install_requires: kwargs["install_requires"] = install_requires @@ -162,7 +160,7 @@ def install(**kwargs): if easy_install_lib: cmdclass["easy_install"] = easy_install return setup( - name=distname, + name="pylint", version=__pkginfo__["version"], license=__pkginfo__["license"], description=__pkginfo__["description"], From 1daefad7fa09092778c79af9b519e72e2024d6ab Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 30 May 2019 09:29:58 +0200 Subject: [PATCH 0125/5147] Add test to prove that issue is fixed. Close #2939 --- pylint/test/functional/assigning_non_slot.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pylint/test/functional/assigning_non_slot.py b/pylint/test/functional/assigning_non_slot.py index 894f172997..6fd299f375 100644 --- a/pylint/test/functional/assigning_non_slot.py +++ b/pylint/test/functional/assigning_non_slot.py @@ -156,3 +156,20 @@ class ClassHavingUnknownAncestors(Unknown): def test(self): self.not_yo = 42 + + +# pylint: disable=wrong-import-order, wrong-import-position +from typing import ( + Generic, + TypeVar, +) + +TYPE = TypeVar('TYPE') + + +class Cls(Generic[TYPE]): + """ Simple class with slots """ + __slots__ = ['value'] + + def __init__(self, value): + self.value = value From 7cc9da97285deb3b0213c9f2b403ae21f7b5c5c2 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 30 May 2019 19:34:47 +0200 Subject: [PATCH 0126/5147] Allow bytes to be interpreted correctly for logging-too-many-args check The code was assuming that a string will be passed, not bytes, which resulted in a false positive. Close #2938 --- pylint/checkers/logging.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pylint/checkers/logging.py b/pylint/checkers/logging.py index 3d979da9b2..dd9de9a552 100644 --- a/pylint/checkers/logging.py +++ b/pylint/checkers/logging.py @@ -287,8 +287,11 @@ def _check_format_string(self, node, format_arg): # 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": From 8d7a02784dfc3eb533b44779cc87b6f90aa47236 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sat, 1 Jun 2019 09:27:19 +0200 Subject: [PATCH 0127/5147] Only unpack the proxied function for bound methods with unbound methods --- pylint/checkers/typecheck.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 85454c4a05..aaafaae59a 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -967,7 +967,9 @@ def visit_assign(self, node): ): return - if isinstance(function_node, astroid.BoundMethod): + if isinstance(function_node, astroid.BoundMethod) and isinstance( + function_node._proxied, astroid.UnboundMethod + ): # Unwrap to get the actual function object function_node = function_node._proxied._proxied From 8f8587edff4d30c1437c5ea664bcf52f5715ad01 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sat, 1 Jun 2019 09:38:59 +0200 Subject: [PATCH 0128/5147] Remove an occurrence of a check that no longer exists. Close #2931 --- pylint/checkers/exceptions.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index a89c5f5c78..d742e4c1b0 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -231,8 +231,6 @@ 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) - else: - self._checker.add_message("nonstandard-exception", node=self._node) def visit_tuple(self, tuple_node): if PY3K or not tuple_node.elts: @@ -290,7 +288,6 @@ def open(self): super(ExceptionsChecker, self).open() @utils.check_messages( - "nonstandard-exception", "misplaced-bare-raise", "raising-bad-type", "raising-non-exception", From d14440fe280a25439c76e53455b4831e04c4bb17 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sat, 1 Jun 2019 11:30:42 +0200 Subject: [PATCH 0129/5147] Now that we support IfExp inference, address a test --- pylint/test/extensions/data/redefined.py | 2 +- pylint/test/extensions/test_redefined.py | 28 ++++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/pylint/test/extensions/data/redefined.py b/pylint/test/extensions/data/redefined.py index 7db75e8ed6..8829c4383c 100644 --- a/pylint/test/extensions/data/redefined.py +++ b/pylint/test/extensions/data/redefined.py @@ -18,7 +18,7 @@ def __init__(self): self.a_str = "hello" a_str = False (a_str, b_str) = (1, 2) # no support for inference on tuple assignment - a_str = 2.0 if self.var else 1.0 # no support for inference on ifexpr + a_str = 2.0 if self.var else 1.0 def _getter(self): return self.a_str diff --git a/pylint/test/extensions/test_redefined.py b/pylint/test/extensions/test_redefined.py index 85871b064f..da35b06253 100644 --- a/pylint/test/extensions/test_redefined.py +++ b/pylint/test/extensions/test_redefined.py @@ -15,15 +15,16 @@ from pylint.lint import fix_import_path EXPECTED = [ - 'Redefinition of self.var1 type from int to float', - 'Redefinition of var type from int to str', - 'Redefinition of myint type from int to bool', - 'Redefinition of _OK type from bool to str', - 'Redefinition of instance type from redefined.MyClass to bool', - 'Redefinition of SOME_FLOAT type from float to int', - 'Redefinition of var3 type from str to int', - 'Redefinition of var type from bool to int', - 'Redefinition of var4 type from float to str', + "Redefinition of self.var1 type from int to float", + "Redefinition of a_str type from bool to float", + "Redefinition of var type from int to str", + "Redefinition of myint type from int to bool", + "Redefinition of _OK type from bool to str", + "Redefinition of instance type from redefined.MyClass to bool", + "Redefinition of SOME_FLOAT type from float to int", + "Redefinition of var3 type from str to int", + "Redefinition of var type from bool to int", + "Redefinition of var4 type from float to str", ] @@ -34,16 +35,15 @@ def checker(checker): @pytest.fixture(scope="module") def disable(disable): - return ['I'] + return ["I"] def test_types_redefined(linter): - elif_test = osp.join(osp.dirname(osp.abspath(__file__)), 'data', - 'redefined.py') + elif_test = osp.join(osp.dirname(osp.abspath(__file__)), "data", "redefined.py") with fix_import_path([elif_test]): linter.check([elif_test]) msgs = sorted(linter.reporter.messages, key=lambda item: item.line) - assert len(msgs) == 9 + assert len(msgs) == 10 for msg, expected in zip(msgs, EXPECTED): - assert msg.symbol == 'redefined-variable-type' + assert msg.symbol == "redefined-variable-type" assert msg.msg == expected From a0a178cab6b7c823ca0aba2307723e7abe6ab516 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sat, 1 Jun 2019 11:33:38 +0200 Subject: [PATCH 0130/5147] Add test for fixed issue. Close #2937 --- .../test/functional/regression_2937_ifexp.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 pylint/test/functional/regression_2937_ifexp.py diff --git a/pylint/test/functional/regression_2937_ifexp.py b/pylint/test/functional/regression_2937_ifexp.py new file mode 100644 index 0000000000..55b240440e --- /dev/null +++ b/pylint/test/functional/regression_2937_ifexp.py @@ -0,0 +1,20 @@ +# pylint: disable=missing-docstring,no-else-return,using-constant-test +def get_list(): + return [1] if True else [2] + + +def find_int(): + return int(get_list()[0]) + + +def func(): + if True: + return find_int() + else: + return find_int() + + +def test(): + resp = func() + assert resp / resp > 0 + return resp From e3d4e80ca9be6b61d7c9773f8a20661a52fa9ed9 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 2 Jun 2019 12:02:12 +0200 Subject: [PATCH 0131/5147] Add test for regression. Close #2443 --- .../regression_2443_duplicate_bases.py | 28 +++++++++++++++++++ .../regression_2443_duplicate_bases.rc | 2 ++ 2 files changed, 30 insertions(+) create mode 100644 pylint/test/functional/regression_2443_duplicate_bases.py create mode 100644 pylint/test/functional/regression_2443_duplicate_bases.rc diff --git a/pylint/test/functional/regression_2443_duplicate_bases.py b/pylint/test/functional/regression_2443_duplicate_bases.py new file mode 100644 index 0000000000..f32490c44a --- /dev/null +++ b/pylint/test/functional/regression_2443_duplicate_bases.py @@ -0,0 +1,28 @@ +# pylint: disable=missing-docstring, too-many-ancestors,too-few-public-methods +from typing import Generic, TypeVar + +IN = TypeVar('IN', contravariant=True) +OUT = TypeVar('OUT', covariant=True) + + +class Service: + pass + + +class ConsumingMixin(Generic[IN]): + pass + + +class ProducingMixin(Generic[OUT]): + pass + + +class StreamingMixin(Generic[IN, OUT], ConsumingMixin[IN], ProducingMixin[OUT]): + pass + + +class Example(StreamingMixin[str, int], Service): + pass + + +print(Example.__mro__) diff --git a/pylint/test/functional/regression_2443_duplicate_bases.rc b/pylint/test/functional/regression_2443_duplicate_bases.rc new file mode 100644 index 0000000000..0ba2b6333d --- /dev/null +++ b/pylint/test/functional/regression_2443_duplicate_bases.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.6 From 5af3758ae0e7377bf8ddde5347f5f456657db6ce Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Tue, 4 Jun 2019 08:20:32 +0200 Subject: [PATCH 0132/5147] Use Sequence from collections.abc --- pylint/checkers/typecheck.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index aaafaae59a..8fa7befe3c 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -35,7 +35,6 @@ """ import builtins -import collections import fnmatch import heapq import itertools @@ -44,6 +43,7 @@ import shlex import sys import types +from collections.abc import Sequence from functools import singledispatch import astroid @@ -82,7 +82,7 @@ def _unflatten(iterable): for index, elem in enumerate(iterable): - if isinstance(elem, collections.Sequence) and not isinstance(elem, str): + if isinstance(elem, Sequence) and not isinstance(elem, str): for single_elem in _unflatten(elem): yield single_elem elif elem and not index: From 8582756731c83142dc8c387de2026edc9bd4969f Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 20 Jun 2019 09:23:44 +0200 Subject: [PATCH 0133/5147] Ignore ``consider-using-dict-comprehension`` for constructions that can't be converted to a comprehension Close #2963 --- ChangeLog | 4 ++++ pylint/checkers/refactoring.py | 13 ++++++++++--- .../functional/consider_using_dict_comprehension.py | 3 +++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5afba3ebc6..459ff154f7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -152,6 +152,10 @@ Release date: TBA Close #1581 +* Fixed a false positive with ``consider-using-dict-comprehension`` for constructions that can't be converted to a comprehension + + Close #2963 + What's New in Pylint 2.3.0? =========================== diff --git a/pylint/checkers/refactoring.py b/pylint/checkers/refactoring.py index cab521aaac..27a85f5633 100644 --- a/pylint/checkers/refactoring.py +++ b/pylint/checkers/refactoring.py @@ -237,6 +237,8 @@ class RefactoringChecker(checkers.BaseTokenChecker): "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 " @@ -617,11 +619,16 @@ def _check_consider_using_comprehension_constructor(self, node): if ( isinstance(node.func, astroid.Name) and node.args - and node.func.name in {"dict", "set"} and isinstance(node.args[0], astroid.ListComp) ): - message_name = "consider-using-{}-comprehension".format(node.func.name) - self.add_message(message_name, node=node) + 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", diff --git a/pylint/test/functional/consider_using_dict_comprehension.py b/pylint/test/functional/consider_using_dict_comprehension.py index dd70be21d3..da0be6d5a7 100644 --- a/pylint/test/functional/consider_using_dict_comprehension.py +++ b/pylint/test/functional/consider_using_dict_comprehension.py @@ -7,3 +7,6 @@ dict([]) dict([(number, number*2) for number in numbers]) # [consider-using-dict-comprehension] + +# Cannot emit as this cannot be written as a comprehension +dict([value.split("=") for value in ["a=b", "c=d"]]) From a913613257c9d14245a780695c113400c862cae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Thu, 20 Jun 2019 09:34:10 +0300 Subject: [PATCH 0134/5147] Fix mypy 0.710 errors --- pylint/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylint/config.py b/pylint/config.py index 9ccc1f28cc..ebd1e56151 100644 --- a/pylint/config.py +++ b/pylint/config.py @@ -46,6 +46,7 @@ import re import sys import time +from typing import Any, Dict, Tuple from pylint import utils @@ -796,7 +797,7 @@ class OptionsProviderMixIn: # those attributes should be overridden priority = -1 name = "default" - options = () + options = () # type: Tuple[Tuple[str, Dict[str, Any]], ...] level = 0 def __init__(self): From 927db96bbd343a61428faa0249fdeec58cfb3ebe Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 20 Jun 2019 09:47:50 +0200 Subject: [PATCH 0135/5147] Dropped support for Python 3.4 It reached EoL a while ago an our typed_ast dependency also stopped working for 3.4. Ideally just use a more recent Python version, which also helps us as we don't have to maintain support for multiple minor versions at once. --- .travis.yml | 3 --- ChangeLog | 2 ++ doc/faq.rst | 4 ++-- pylint/__pkginfo__.py | 1 - setup.py | 2 +- tox.ini | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6ee6e78f9b..a9eedab1b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,9 +17,6 @@ jobs: env: TOXENV=formatting - python: 3.6 env: TOXENV=mypy - - stage: tests-cpython - python: 3.4 - env: TOXENV=py34 - python: 3.5-dev env: TOXENV=py35 - python: 3.6 diff --git a/ChangeLog b/ChangeLog index 459ff154f7..69d517ea26 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,8 @@ Release date: TBA Close #2928 +* Dropped support for Python 3.4. + * ``assignment-from-no-return`` not triggered for async methods. Close #2902 diff --git a/doc/faq.rst b/doc/faq.rst index 30d4a7d269..46cf260c5d 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -50,9 +50,9 @@ supported. 2.4 What versions of Python is Pylint supporting? -------------------------------------------------- -Since Pylint 2.0, the supported running environment is Python 3.4+. +Since Pylint 2.X, the supported running environment is Python 3.5+. -That is, Pylint 2.0 is still able to analyze Python 2 files, but some +That is, Pylint 2.X is still able to analyze Python 2 files, but some specific checks might not work, as they would assume that their running environment was Python 2. diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index f7082f097d..a6752a1783 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -53,7 +53,6 @@ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", diff --git a/setup.py b/setup.py index bcf38bf617..7d9f883420 100644 --- a/setup.py +++ b/setup.py @@ -175,7 +175,7 @@ def install(**kwargs): cmdclass=cmdclass, extras_require=extras_require, test_suite="test", - python_requires=">=3.4.*", + python_requires=">=3.5.*", setup_requires=["pytest-runner"], tests_require=["pytest"], **kwargs diff --git a/tox.ini b/tox.ini index 26f1e2fb37..5d58ddde28 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py34, py35, py36, py37, py38, pypy, pylint +envlist = py35, py36, py37, py38, pypy, pylint skip_missing_interpreters = true [testenv:pylint] From 7081d91f30728653000bdfc59ea85a3395f96418 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 20 Jun 2019 09:51:50 +0200 Subject: [PATCH 0136/5147] Drop support for 3.4 in appveyor and use 3.7 instead --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 06332eb0fe..7db2120f40 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,15 +4,15 @@ cache: - 'C:\\tmp' environment: matrix: - - PYTHON: "C:\\Python34" - TOXENV: "py34" - - PYTHON: "C:\\Python35" TOXENV: "py35" - PYTHON: "C:\\Python36" TOXENV: "py36" + - PYTHON: "C:\\Python37" + TOXENV: "py37" + init: - ps: echo $env:TOXENV - ps: ls C:\Python* From 33b8185a455c1686d038258697bb93005f2441c2 Mon Sep 17 00:00:00 2001 From: Ashley Whetter Date: Fri, 14 Jun 2019 22:28:42 -0700 Subject: [PATCH 0137/5147] Stopped installing tests with package --- MANIFEST.in | 8 ++-- pylint/__pkginfo__.py | 2 - setup.cfg | 6 +++ setup.py | 32 +-------------- .../test => tests}/acceptance/test_stdlib.py | 0 {pylint/test => tests}/conftest.py | 0 {pylint/test => tests}/data/__init__.py | 0 {pylint/test => tests}/data/ascript | 0 .../test => tests}/data/classes_No_Name.dot | 0 .../test => tests}/data/clientmodule_test.py | 0 .../test => tests}/data/packages_No_Name.dot | 0 .../data/suppliermodule_test.py | 0 {pylint/test => tests}/extensions/__init__.py | 0 .../extensions/data/bad_builtin.py | 0 .../extensions/data/broad_try_clause.py | 0 .../extensions/data/compare_to_zero.py | 0 .../extensions/data/docstring.py | 0 .../test => tests}/extensions/data/elif.py | 0 .../data/empty_string_comparison.py | 0 .../test => tests}/extensions/data/mccabe.py | 0 .../extensions/data/overlapping_exceptions.py | 0 .../data/overlapping_exceptions_py33.py | 0 .../extensions/data/redefined.py | 0 .../extensions/test_bad_builtin.py | 0 .../extensions/test_broad_try_clause.py | 0 .../extensions/test_check_docs.py | 0 .../extensions/test_check_docs_utils.py | 0 .../extensions/test_check_mccabe.py | 0 .../extensions/test_check_raise_docs.py | 0 .../extensions/test_check_return_docs.py | 0 .../extensions/test_check_yields_docs.py | 0 .../extensions/test_comparetozero.py | 0 .../extensions/test_docstyle.py | 0 .../extensions/test_elseif_used.py | 0 .../extensions/test_emptystring.py | 0 .../extensions/test_overlapping_exceptions.py | 0 .../extensions/test_redefined.py | 0 {pylint/test => tests}/functional/__init__.py | 0 .../functional/abstract_abc_methods.py | 0 .../abstract_class_instantiated_in_class.py | 0 .../abstract_class_instantiated_py2.py | 0 .../abstract_class_instantiated_py2.rc | 0 .../abstract_class_instantiated_py2.txt | 0 .../abstract_class_instantiated_py3.py | 0 .../abstract_class_instantiated_py3.rc | 0 .../abstract_class_instantiated_py3.txt | 0 .../abstract_class_instantiated_py34.py | 0 .../abstract_class_instantiated_py34.rc | 0 .../abstract_class_instantiated_py34.txt | 0 .../functional/abstract_method_py2.py | 0 .../functional/abstract_method_py2.rc | 0 .../functional/abstract_method_py2.txt | 0 .../functional/abstract_method_py3.py | 0 .../functional/abstract_method_py3.rc | 0 .../functional/abstract_method_py3.txt | 0 .../access_member_before_definition.py | 0 .../access_member_before_definition.txt | 0 .../functional/access_to__name__.py | 0 .../functional/access_to__name__.txt | 0 .../functional/access_to_protected_members.py | 0 .../access_to_protected_members.txt | 0 .../anomalous_unicode_escape_py2.py | 0 .../anomalous_unicode_escape_py2.rc | 0 .../anomalous_unicode_escape_py2.txt | 0 .../anomalous_unicode_escape_py3.py | 0 .../anomalous_unicode_escape_py3.rc | 0 .../anomalous_unicode_escape_py3.txt | 0 .../test => tests}/functional/arguments.py | 0 .../test => tests}/functional/arguments.rc | 0 .../test => tests}/functional/arguments.txt | 0 .../functional/arguments_differ.py | 0 .../functional/arguments_differ.txt | 0 .../functional/arguments_differ_py3.py | 0 .../functional/arguments_differ_py3.rc | 0 .../functional/arguments_differ_py3.txt | 0 .../functional/assert_on_tuple.py | 0 .../functional/assert_on_tuple.txt | 0 .../functional/assigning_non_slot.py | 0 .../functional/assigning_non_slot.txt | 0 .../functional/assignment_from_no_return.py | 0 .../functional/assignment_from_no_return.txt | 0 .../assignment_from_no_return_py3.py | 0 .../assignment_from_no_return_py3.rc | 0 .../assignment_from_no_return_py3.txt | 0 .../functional/async_functions.py | 0 .../functional/async_functions.rc | 0 .../functional/async_functions.txt | 0 .../attribute_defined_outside_init.py | 0 .../attribute_defined_outside_init.txt | 0 .../functional/bad_continuation.py | 0 .../functional/bad_continuation.txt | 0 .../functional/bad_continuation_py36.py | 0 .../functional/bad_continuation_py36.rc | 0 .../functional/bad_continuation_tabs.py | 0 .../functional/bad_continuation_tabs.rc | 0 .../functional/bad_continuation_tabs.txt | 0 .../functional/bad_except_order.py | 0 .../functional/bad_except_order.txt | 0 .../functional/bad_exception_context.py | 0 .../functional/bad_exception_context.rc | 0 .../functional/bad_exception_context.txt | 0 .../functional/bad_indentation.py | 0 .../functional/bad_indentation.txt | 0 .../functional/bad_inline_option.py | 0 .../functional/bad_inline_option.rc | 0 .../functional/bad_inline_option.txt | 0 .../functional/bad_open_mode.py | 0 .../functional/bad_open_mode.rc | 0 .../functional/bad_open_mode.txt | 0 .../functional/bad_open_mode_py3.py | 0 .../functional/bad_open_mode_py3.rc | 0 .../functional/bad_open_mode_py3.txt | 0 .../functional/bad_reversed_sequence.py | 0 .../functional/bad_reversed_sequence.txt | 0 .../functional/bad_staticmethod_argument.py | 0 .../functional/bad_staticmethod_argument.txt | 0 .../functional/bad_thread_instantiation.py | 0 .../functional/bad_thread_instantiation.txt | 0 .../functional/bad_whitespace.py | 0 .../functional/bad_whitespace.txt | 0 .../test => tests}/functional/bare_except.py | 0 .../test => tests}/functional/bare_except.txt | 0 .../functional/blacklisted_name.py | 0 .../functional/blacklisted_name.txt | 0 .../functional/boolean_datetime.py | 0 .../functional/boolean_datetime.rc | 0 .../functional/boolean_datetime.txt | 0 .../test => tests}/functional/broad_except.py | 0 .../functional/broad_except.txt | 0 .../bugfix_local_scope_metaclass_1177.py | 0 .../bugfix_local_scope_metaclass_1177.rc | 0 .../functional/cellvar_escaping_loop.py | 0 .../functional/cellvar_escaping_loop.txt | 0 .../functional/class_members_py27.py | 0 .../functional/class_members_py27.rc | 0 .../functional/class_members_py27.txt | 0 .../functional/class_members_py30.py | 0 .../functional/class_members_py30.rc | 0 .../functional/class_members_py30.txt | 0 .../test => tests}/functional/class_scope.py | 0 .../test => tests}/functional/class_scope.txt | 0 .../functional/comparison_with_callable.py | 0 .../functional/comparison_with_callable.txt | 0 .../functional/confidence_filter.py | 0 .../functional/confidence_filter.rc | 0 .../functional/confidence_filter.txt | 0 .../functional/confusing_with_statement.py | 0 .../functional/confusing_with_statement.txt | 0 .../consider_iterating_dictionary.py | 0 .../consider_iterating_dictionary.txt | 0 .../functional/consider_join.py | 0 .../functional/consider_join.txt | 0 .../functional/consider_merging_isinstance.py | 0 .../consider_merging_isinstance.txt | 0 .../functional/consider_swap_variables.py | 0 .../functional/consider_swap_variables.txt | 0 .../consider_using_dict_comprehension.py | 0 .../consider_using_dict_comprehension.txt | 0 .../functional/consider_using_enumerate.py | 0 .../functional/consider_using_enumerate.txt | 0 .../functional/consider_using_get.py | 0 .../functional/consider_using_get.txt | 0 .../functional/consider_using_in.py | 0 .../functional/consider_using_in.txt | 0 .../consider_using_set_comprehension.py | 0 .../consider_using_set_comprehension.txt | 0 .../functional/continue_in_finally.py | 0 .../functional/continue_in_finally.txt | 0 .../functional/control_pragmas.py | 0 .../functional/control_pragmas.txt | 0 .../functional/crash_missing_module_type.py | 0 .../functional/crash_missing_module_type.txt | 0 .../functional/ctor_arguments.py | 0 .../functional/ctor_arguments.txt | 0 .../functional/dangerous_default_value.py | 0 .../functional/dangerous_default_value.rc | 0 .../functional/dangerous_default_value.txt | 0 .../dangerous_default_value_py30.py | 0 .../dangerous_default_value_py30.rc | 0 .../dangerous_default_value_py30.txt | 0 .../defined_and_used_on_same_line.py | 0 .../functional/deprecated_lambda.py | 0 .../functional/deprecated_lambda.rc | 0 .../functional/deprecated_lambda.txt | 0 .../deprecated_method_getmoduleinfo.py | 0 .../deprecated_method_getmoduleinfo.rc | 0 .../deprecated_method_getmoduleinfo.txt | 0 .../functional/deprecated_methods_py2.py | 0 .../functional/deprecated_methods_py2.rc | 0 .../functional/deprecated_methods_py2.txt | 0 .../functional/deprecated_methods_py3.py | 0 .../functional/deprecated_methods_py3.rc | 0 .../functional/deprecated_methods_py3.txt | 0 .../functional/deprecated_methods_py36.py | 0 .../functional/deprecated_methods_py36.rc | 0 .../functional/deprecated_methods_py36.txt | 0 .../functional/deprecated_methods_py38.py | 0 .../functional/deprecated_methods_py38.rc | 0 .../functional/deprecated_methods_py38.txt | 0 .../functional/deprecated_module_py2.py | 0 .../functional/deprecated_module_py2.rc | 0 .../functional/deprecated_module_py2.txt | 0 .../functional/deprecated_module_py3.py | 0 .../functional/deprecated_module_py3.rc | 0 .../functional/deprecated_module_py3.txt | 0 .../functional/deprecated_module_py36.py | 0 .../functional/deprecated_module_py36.rc | 0 .../functional/deprecated_module_py36.txt | 0 .../functional/deprecated_module_py4.py | 0 .../functional/deprecated_module_py4.rc | 0 .../functional/deprecated_module_py4.txt | 0 .../deprecated_module_uninstalled.py | 0 .../deprecated_module_uninstalled.rc | 0 .../deprecated_module_uninstalled.txt | 0 .../functional/dict_iter_missing_items.py | 0 .../functional/dict_iter_missing_items.txt | 0 .../disable_msg_github_issue_1389.py | 0 .../disable_msg_github_issue_1389.rc | 0 .../functional/disable_ungrouped_imports.py | 0 .../functional/disable_ungrouped_imports.txt | 0 .../functional/disable_wrong_import_order.py | 0 .../functional/disable_wrong_import_order.txt | 0 .../disable_wrong_import_position.py | 0 .../test => tests}/functional/docstrings.py | 0 .../test => tests}/functional/docstrings.txt | 0 .../functional/duplicate_argument_name.py | 0 .../functional/duplicate_argument_name.txt | 0 .../functional/duplicate_argument_name_py3.py | 0 .../functional/duplicate_argument_name_py3.rc | 0 .../duplicate_argument_name_py3.txt | 0 .../functional/duplicate_bases.py | 0 .../functional/duplicate_bases.txt | 0 .../functional/duplicate_dict_literal_key.py | 0 .../functional/duplicate_dict_literal_key.txt | 0 .../functional/duplicate_except.py | 0 .../functional/duplicate_except.txt | 0 .../duplicate_string_formatting_argument.py | 0 .../duplicate_string_formatting_argument.txt | 0 .../test => tests}/functional/eval_used.py | 0 .../test => tests}/functional/eval_used.txt | 0 .../functional/exception_is_binary_op.py | 0 .../functional/exception_is_binary_op.txt | 0 .../functional/exception_message.py | 0 .../functional/exception_message.rc | 0 .../functional/exception_message.txt | 0 .../functional/exec_used_py2.py | 0 .../functional/exec_used_py2.rc | 0 .../functional/exec_used_py2.txt | 0 .../functional/exec_used_py3.py | 0 .../functional/exec_used_py3.rc | 0 .../functional/exec_used_py3.txt | 0 .../functional/fallback_import_disabled.py | 0 .../functional/fallback_import_disabled.txt | 0 .../functional/fallback_import_enabled.py | 0 .../functional/fallback_import_enabled.rc | 0 .../functional/fallback_import_enabled.txt | 0 {pylint/test => tests}/functional/fixme.py | 0 {pylint/test => tests}/functional/fixme.txt | 0 .../functional/fixme_bad_formatting_1139.py | 0 .../functional/fixme_bad_formatting_1139.rc | 0 .../functional/fixme_bad_formatting_1139.txt | 0 .../formatted_string_literal_with_if_py36.py | 0 .../formatted_string_literal_with_if_py36.rc | 0 .../test => tests}/functional/formatting.txt | 0 .../functional/function_redefined.py | 0 .../functional/function_redefined.txt | 0 .../functional/future_import.py | 0 .../functional/future_unicode_literals.py | 0 .../functional/future_unicode_literals.rc | 0 .../functional/future_unicode_literals.txt | 0 .../functional/generated_members.py | 0 .../functional/generated_members.rc | 0 .../functional/genexp_in_class_scope.py | 0 .../functional/genexp_in_class_scope.txt | 0 .../functional/genexpr_variable_scope.py | 0 .../functional/genexpr_variable_scope.txt | 0 {pylint/test => tests}/functional/globals.py | 0 {pylint/test => tests}/functional/globals.txt | 0 .../implicit_str_concat_in_sequence.py | 0 .../implicit_str_concat_in_sequence.txt | 0 .../implicit_str_concat_in_sequence_latin1.py | 0 ...implicit_str_concat_in_sequence_latin1.txt | 0 ...plicit_str_concat_in_sequence_multiline.py | 0 ...plicit_str_concat_in_sequence_multiline.rc | 0 ...licit_str_concat_in_sequence_multiline.txt | 0 .../implicit_str_concat_in_sequence_utf8.py | 0 .../implicit_str_concat_in_sequence_utf8.txt | 0 .../test => tests}/functional/import_error.py | 0 .../test => tests}/functional/import_error.rc | 0 .../functional/import_error.txt | 0 .../functional/inconsistent_mro.py | 0 .../functional/inconsistent_mro.txt | 0 .../functional/inconsistent_returns.py | 0 .../functional/inconsistent_returns.rc | 0 .../functional/inconsistent_returns.txt | 0 .../functional/indexing_exception.py | 0 .../functional/indexing_exception.rc | 0 .../functional/indexing_exception.txt | 0 .../functional/inherit_non_class.py | 0 .../functional/inherit_non_class.txt | 0 .../functional/init_is_generator.py | 0 .../functional/init_is_generator.txt | 0 .../functional/init_not_called.py | 0 .../functional/init_not_called.txt | 0 .../init_subclass_classmethod_py36.py | 0 .../init_subclass_classmethod_py36.rc | 0 .../functional/invalid_all_object.py | 0 .../functional/invalid_all_object.txt | 0 .../functional/invalid_encoded_data.py | 0 .../functional/invalid_encoded_data.rc | 0 .../functional/invalid_encoded_data.txt | 0 .../functional/invalid_encoding_py27.py | 0 .../functional/invalid_encoding_py27.rc | 0 .../functional/invalid_encoding_py27.txt | 0 .../functional/invalid_envvar_value.py | 0 .../functional/invalid_envvar_value.txt | 0 .../functional/invalid_exceptions_caught.py | 0 .../functional/invalid_exceptions_caught.txt | 0 .../functional/invalid_exceptions_raised.py | 0 .../functional/invalid_exceptions_raised.txt | 0 .../functional/invalid_length_returned.py | 0 .../functional/invalid_length_returned.txt | 0 .../functional/invalid_metaclass.py | 0 .../functional/invalid_metaclass.txt | 0 .../functional/invalid_metaclass_py3.py | 0 .../functional/invalid_metaclass_py3.rc | 0 .../functional/invalid_metaclass_py3.txt | 0 .../test => tests}/functional/invalid_name.py | 0 .../functional/invalid_name.txt | 0 .../functional/invalid_sequence_index.py | 0 .../functional/invalid_sequence_index.txt | 0 .../functional/invalid_slice_index.py | 0 .../functional/invalid_slice_index.txt | 0 .../invalid_star_assignment_target.py | 0 .../invalid_star_assignment_target.rc | 0 .../invalid_star_assignment_target.txt | 0 .../functional/invalid_unary_operand_type.py | 0 .../functional/invalid_unary_operand_type.txt | 0 .../functional/iterable_context.py | 0 .../functional/iterable_context.txt | 0 .../functional/iterable_context_py2.py | 0 .../functional/iterable_context_py2.rc | 0 .../functional/iterable_context_py2.txt | 0 .../functional/iterable_context_py3.py | 0 .../functional/iterable_context_py3.rc | 0 .../functional/iterable_context_py3.txt | 0 .../functional/iterable_context_py36.py | 0 .../functional/iterable_context_py36.rc | 0 .../functional/iterable_context_py36.txt | 0 .../functional/keyword_arg_before_vararg.py | 0 .../functional/keyword_arg_before_vararg.txt | 0 .../test => tests}/functional/len_checks.py | 0 .../test => tests}/functional/len_checks.txt | 0 .../test => tests}/functional/line_endings.py | 0 .../test => tests}/functional/line_endings.rc | 0 .../functional/line_endings.txt | 0 .../functional/line_too_long.py | 0 .../functional/line_too_long.txt | 0 .../functional/line_too_long_end_of_module.py | 0 .../functional/literal_comparison.py | 0 .../functional/literal_comparison.txt | 0 .../logging_format_interpolation.py | 0 .../logging_format_interpolation.txt | 0 .../logging_format_interpolation_py36.py | 0 .../logging_format_interpolation_py36.rc | 0 .../logging_format_interpolation_py36.txt | 0 .../logging_fstring_interpolation_py36.py | 0 .../logging_fstring_interpolation_py36.rc | 0 .../logging_fstring_interpolation_py36.txt | 0 .../functional/logging_not_lazy.py | 0 .../functional/logging_not_lazy.txt | 0 .../functional/logical_tautology.py | 0 .../functional/logical_tautology.txt | 0 .../functional/long_lines_with_utf8.py | 0 .../functional/long_lines_with_utf8.txt | 0 .../functional/long_utf8_lines.py | 0 .../functional/long_utf8_lines.txt | 0 .../functional/lost_exception.py | 0 .../functional/lost_exception.txt | 0 .../functional/mapping_context.py | 0 .../functional/mapping_context.txt | 0 .../functional/mapping_context_py2.py | 0 .../functional/mapping_context_py2.rc | 0 .../functional/mapping_context_py2.txt | 0 .../functional/mapping_context_py3.py | 0 .../functional/mapping_context_py3.rc | 0 .../functional/mapping_context_py3.txt | 0 .../functional/member_checks.py | 0 .../functional/member_checks.txt | 0 .../functional/member_checks_hints.py | 0 .../functional/member_checks_hints.rc | 0 .../functional/member_checks_hints.txt | 0 .../functional/member_checks_ignore_none.py | 0 .../functional/member_checks_ignore_none.rc | 0 .../functional/member_checks_ignore_none.txt | 0 .../member_checks_inference_improvements.py | 0 .../functional/member_checks_no_hints.py | 0 .../functional/member_checks_no_hints.rc | 0 .../functional/member_checks_no_hints.txt | 0 .../functional/member_checks_opaque.py | 0 .../functional/member_checks_opaque.rc | 0 .../functional/member_checks_opaque.txt | 0 .../functional/member_checks_py37.py | 0 .../functional/member_checks_py37.rc | 0 .../functional/member_checks_py37.txt | 0 .../functional/membership_protocol.py | 0 .../functional/membership_protocol.txt | 0 .../functional/membership_protocol_py2.py | 0 .../functional/membership_protocol_py2.rc | 0 .../functional/membership_protocol_py2.txt | 0 .../functional/membership_protocol_py3.py | 0 .../functional/membership_protocol_py3.rc | 0 .../functional/membership_protocol_py3.txt | 0 .../functional/messages_managed_by_id.py | 0 .../functional/messages_managed_by_id.txt | 0 .../functional/method_hidden.py | 0 .../functional/method_hidden.txt | 0 .../functional/misplaced_bare_raise.py | 0 .../functional/misplaced_bare_raise.txt | 0 .../misplaced_comparison_constant.py | 0 .../misplaced_comparison_constant.txt | 0 .../functional/misplaced_format_function.py | 0 .../functional/misplaced_format_function.txt | 0 .../functional/misplaced_future.py | 0 .../functional/misplaced_future.txt | 0 .../functional/missing_docstring.py | 0 .../functional/missing_docstring.txt | 0 .../functional/missing_final_newline.py | 0 .../functional/missing_final_newline.txt | 0 .../functional/missing_kwoa_py3.py | 0 .../functional/missing_kwoa_py3.rc | 0 .../functional/missing_kwoa_py3.txt | 0 .../missing_parentheses_for_call_in_test.py | 0 .../missing_parentheses_for_call_in_test.txt | 0 .../functional/missing_self_argument.py | 0 .../functional/missing_self_argument.txt | 0 .../functional/mixed_indentation.py | 0 .../functional/mixed_indentation.txt | 0 .../functional/monkeypatch_method.py | 0 .../functional/monkeypatch_method.txt | 0 .../functional/multiple_imports.py | 0 .../functional/multiple_imports.txt | 0 .../functional/namePresetCamelCase.py | 0 .../functional/namePresetCamelCase.rc | 0 .../functional/namePresetCamelCase.txt | 0 .../functional/name_preset_snake_case.py | 0 .../functional/name_preset_snake_case.rc | 0 .../functional/name_preset_snake_case.txt | 0 .../test => tests}/functional/name_styles.py | 0 .../test => tests}/functional/name_styles.rc | 0 .../test => tests}/functional/name_styles.txt | 0 .../functional/namedtuple_member_inference.py | 0 .../namedtuple_member_inference.txt | 0 .../functional/names_in__all__.py | 0 .../functional/names_in__all__.txt | 0 .../functional/nested_blocks_issue1088.py | 0 .../functional/nested_blocks_issue1088.txt | 0 .../functional/nested_func_defined_in_loop.py | 0 .../functional/no_classmethod_decorator.py | 0 .../functional/no_classmethod_decorator.txt | 0 .../functional/no_else_raise.py | 0 .../functional/no_else_raise.txt | 0 .../functional/no_else_return.py | 0 .../functional/no_else_return.txt | 0 .../functional/no_name_in_module.py | 0 .../functional/no_name_in_module.txt | 0 .../functional/no_self_argument_py37.py | 0 .../functional/no_self_argument_py37.rc | 0 .../functional/no_self_argument_py37.txt | 0 .../test => tests}/functional/no_self_use.py | 0 .../test => tests}/functional/no_self_use.txt | 0 .../functional/no_self_use_py3.py | 0 .../functional/no_self_use_py3.rc | 0 .../functional/no_self_use_py3.txt | 0 .../functional/no_staticmethod_decorator.py | 0 .../functional/no_staticmethod_decorator.txt | 0 .../functional/non_iterator_returned.py | 0 .../functional/non_iterator_returned.txt | 0 .../functional/none_dunder_protocols_py36.py | 0 .../functional/none_dunder_protocols_py36.rc | 0 .../functional/none_dunder_protocols_py36.txt | 0 .../functional/nonexistent_operator.py | 0 .../functional/nonexistent_operator.txt | 0 .../functional/nonlocal_and_global.py | 0 .../functional/nonlocal_and_global.rc | 0 .../functional/nonlocal_and_global.txt | 0 .../functional/nonlocal_without_binding.py | 0 .../functional/nonlocal_without_binding.rc | 0 .../functional/nonlocal_without_binding.txt | 0 .../functional/not_async_context_manager.py | 0 .../functional/not_async_context_manager.rc | 0 .../functional/not_async_context_manager.txt | 0 .../not_async_context_manager_py37.py | 0 .../not_async_context_manager_py37.rc | 0 .../not_async_context_manager_py37.txt | 0 .../test => tests}/functional/not_callable.py | 0 .../functional/not_callable.txt | 0 .../functional/not_context_manager.py | 0 .../functional/not_context_manager.txt | 0 .../test => tests}/functional/not_in_loop.py | 0 .../test => tests}/functional/not_in_loop.txt | 0 .../functional/old_division_manually.py | 0 .../functional/old_division_manually.rc | 0 .../postponed_evaluation_activated.py | 0 .../postponed_evaluation_activated.rc | 0 .../postponed_evaluation_activated.txt | 0 .../postponed_evaluation_not_activated.py | 0 .../postponed_evaluation_not_activated.rc | 0 .../postponed_evaluation_not_activated.txt | 0 .../functional/pragma_after_backslash.py | 0 .../functional/preferred_module.py | 0 .../functional/preferred_module.rc | 0 .../functional/preferred_module.txt | 0 .../functional/print_always_warns.py | 0 .../functional/print_always_warns.rc | 0 .../functional/print_always_warns.txt | 0 ...rotected_access_access_different_scopes.py | 0 ...rotected_access_access_different_scopes.rc | 0 ...otected_access_access_different_scopes.txt | 0 .../functional/raising_format_tuple.py | 0 .../functional/raising_format_tuple.txt | 0 .../functional/raising_non_exception_py3.py | 0 .../functional/raising_non_exception_py3.rc | 0 .../functional/raising_non_exception_py3.txt | 0 .../test => tests}/functional/raising_self.py | 0 .../functional/raising_self.txt | 0 .../functional/recursion_error_2667.py | 0 .../functional/recursion_error_2667.txt | 0 .../functional/recursion_error_2906.py | 0 .../functional/recursion_error_940.py | 0 .../functional/recursion_error_crash.py | 0 .../functional/recursion_error_crash.txt | 0 .../functional/recursion_error_crash_2683.py | 0 .../functional/recursion_error_crash_2683.txt | 0 .../recursion_error_crash_astroid_623.py | 0 .../recursion_error_crash_astroid_623.txt | 0 .../functional/redefine_in_handler.py | 0 .../functional/redefine_in_handler.rc | 0 .../functional/redefine_in_handler.txt | 0 .../redefined_argument_from_local.py | 0 .../redefined_argument_from_local.txt | 0 .../functional/redefined_builtin.py | 0 .../functional/redefined_builtin.txt | 0 .../functional/redundant_unittest_assert.py | 0 .../functional/redundant_unittest_assert.txt | 0 .../regression_1326_crash_uninferable.py | 0 .../regression_2443_duplicate_bases.py | 0 .../regression_2443_duplicate_bases.rc | 0 .../functional/regression_2937_ifexp.py | 0 .../regression_no_value_for_parameter.py | 0 .../regression_no_value_for_parameter.txt | 0 .../test => tests}/functional/reimported.py | 0 .../test => tests}/functional/reimported.txt | 0 .../functional/repeated_keyword.py | 0 .../functional/repeated_keyword.txt | 0 .../functional/return_in_init.py | 0 .../functional/return_in_init.txt | 0 .../functional/return_outside_function.py | 0 .../functional/return_outside_function.txt | 0 .../functional/reused_outer_loop_variable.py | 0 .../functional/reused_outer_loop_variable.txt | 0 .../reused_outer_loop_variable_py3.py | 0 .../reused_outer_loop_variable_py3.rc | 0 .../reused_outer_loop_variable_py3.txt | 0 .../functional/self_cls_assignment.py | 0 .../functional/self_cls_assignment.txt | 0 .../functional/signature_differs.py | 0 .../functional/signature_differs.txt | 0 .../functional/simplifiable_if_expression.py | 0 .../functional/simplifiable_if_expression.txt | 0 .../functional/simplifiable_if_statement.py | 0 .../functional/simplifiable_if_statement.txt | 0 .../functional/simplify_chained_comparison.py | 0 .../simplify_chained_comparison.txt | 0 .../functional/singledispatch_functions.py | 0 .../functional/singledispatch_functions.rc | 0 .../functional/singledispatch_functions.txt | 0 .../singledispatch_functions_py3.py | 0 .../singledispatch_functions_py3.rc | 0 .../singledispatch_functions_py3.txt | 0 .../functional/singleton_comparison.py | 0 .../functional/singleton_comparison.txt | 0 .../test => tests}/functional/slots_checks.py | 0 .../functional/slots_checks.txt | 0 .../functional/socketerror_import.py | 0 .../star_needs_assignment_target.py | 0 .../star_needs_assignment_target.rc | 0 .../star_needs_assignment_target.txt | 0 .../star_needs_assignment_target_py35.py | 0 .../star_needs_assignment_target_py35.rc | 0 .../star_needs_assignment_target_py35.txt | 0 .../functional/statement_without_effect.py | 0 .../functional/statement_without_effect.txt | 0 .../statement_without_effect_py36.py | 0 .../statement_without_effect_py36.rc | 0 .../statement_without_effect_py36.txt | 0 .../stop_iteration_inside_generator.py | 0 .../stop_iteration_inside_generator.rc | 0 .../stop_iteration_inside_generator.txt | 0 .../functional/string_formatting.py | 0 .../functional/string_formatting.txt | 0 .../functional/string_formatting_disable.py | 0 .../functional/string_formatting_disable.rc | 0 .../functional/string_formatting_disable.txt | 0 .../string_formatting_failed_inference.py | 0 ...string_formatting_failed_inference_py35.py | 0 ...string_formatting_failed_inference_py35.rc | 0 .../functional/string_formatting_py27.py | 0 .../functional/string_formatting_py27.rc | 0 .../functional/string_formatting_py27.txt | 0 .../functional/string_formatting_py3.py | 0 .../functional/string_formatting_py3.rc | 0 .../functional/string_formatting_py3.txt | 0 .../functional/subprocess_popen_preexec_fn.py | 0 .../subprocess_popen_preexec_fn.txt | 0 .../functional/subprocess_run_check35.py | 0 .../functional/subprocess_run_check35.rc | 0 .../functional/subprocess_run_check35.txt | 0 .../test => tests}/functional/super_checks.py | 0 .../functional/super_checks.txt | 0 .../functional/superfluous_parens.py | 0 .../functional/superfluous_parens.txt | 0 .../functional/suspicious_str_strip_call.py | 0 .../functional/suspicious_str_strip_call.rc | 0 .../functional/suspicious_str_strip_call.txt | 0 .../suspicious_str_strip_call_py3.py | 0 .../suspicious_str_strip_call_py3.rc | 0 .../suspicious_str_strip_call_py3.txt | 0 .../test => tests}/functional/syntax_error.py | 0 .../test => tests}/functional/syntax_error.rc | 0 .../functional/syntax_error.txt | 0 .../functional/syntax_error_jython.py | 0 .../functional/syntax_error_jython.rc | 0 .../functional/syntax_error_jython.txt | 0 .../functional/sys_stream_regression_1004.py | 0 .../functional/sys_stream_regression_1004.rc | 0 .../functional/sys_stream_regression_1004.txt | 0 {pylint/test => tests}/functional/ternary.py | 0 {pylint/test => tests}/functional/ternary.txt | 0 .../test => tests}/functional/test_compile.py | 0 .../functional/tokenize_error.py | 0 .../functional/tokenize_error.rc | 0 .../functional/tokenize_error.txt | 0 .../functional/tokenize_error_jython.py | 0 .../functional/tokenize_error_jython.rc | 0 .../functional/tokenize_error_jython.txt | 0 .../functional/too_few_public_methods.py | 0 .../functional/too_few_public_methods.txt | 0 .../functional/too_few_public_methods_37.py | 0 .../functional/too_few_public_methods_37.rc | 0 .../functional/too_few_public_methods_37.txt | 0 .../functional/too_many_ancestors.py | 0 .../functional/too_many_ancestors.txt | 0 .../functional/too_many_arguments.py | 0 .../functional/too_many_arguments.txt | 0 .../too_many_arguments_issue_1045.py | 0 .../too_many_arguments_issue_1045.rc | 0 .../too_many_arguments_issue_1045.txt | 0 .../too_many_boolean_expressions.py | 0 .../too_many_boolean_expressions.txt | 0 .../functional/too_many_branches.py | 0 .../functional/too_many_branches.txt | 0 .../too_many_instance_attributes.py | 0 .../too_many_instance_attributes.txt | 0 .../functional/too_many_lines.py | 0 .../functional/too_many_lines.txt | 0 .../functional/too_many_lines_disabled.py | 0 .../functional/too_many_locals.py | 0 .../functional/too_many_locals.txt | 0 .../functional/too_many_nested_blocks.py | 0 .../functional/too_many_nested_blocks.txt | 0 .../functional/too_many_public_methods.py | 0 .../functional/too_many_public_methods.txt | 0 .../functional/too_many_return_statements.py | 0 .../functional/too_many_return_statements.txt | 0 .../functional/too_many_star_expressions.py | 0 .../functional/too_many_star_expressions.rc | 0 .../functional/too_many_star_expressions.txt | 0 .../functional/too_many_statements.py | 0 .../functional/too_many_statements.txt | 0 .../functional/trailing_comma_tuple.py | 0 .../functional/trailing_comma_tuple.rc | 0 .../functional/trailing_comma_tuple.txt | 0 .../functional/trailing_newlines.py | 0 .../functional/trailing_newlines.txt | 0 .../functional/trailing_whitespaces.py | 0 .../functional/trailing_whitespaces.txt | 0 .../functional/try_except_raise.py | 0 .../functional/try_except_raise.txt | 0 .../functional/try_except_raise_crash.py | 0 .../functional/try_except_raise_crash.txt | 0 .../test => tests}/functional/typing_use.py | 0 .../test => tests}/functional/typing_use.rc | 0 .../test => tests}/functional/typing_use.txt | 0 .../functional/unbalanced_tuple_unpacking.py | 0 .../functional/unbalanced_tuple_unpacking.txt | 0 .../unbalanced_tuple_unpacking_py30.py | 0 .../unbalanced_tuple_unpacking_py30.rc | 0 .../functional/undefined_loop_variable.py | 0 .../functional/undefined_loop_variable.txt | 0 .../functional/undefined_variable.py | 0 .../functional/undefined_variable.txt | 0 .../functional/undefined_variable_py30.py | 0 .../functional/undefined_variable_py30.rc | 0 .../functional/undefined_variable_py30.txt | 0 .../unexpected_special_method_signature.py | 0 .../unexpected_special_method_signature.txt | 0 .../functional/ungrouped_imports.py | 0 .../functional/ungrouped_imports.txt | 0 .../ungrouped_imports_isort_compatible.py | 0 .../ungrouped_imports_isort_compatible.txt | 0 .../functional/unhashable_dict_key.py | 0 .../functional/unhashable_dict_key.txt | 0 .../functional/unidiomatic_typecheck.py | 0 .../functional/unidiomatic_typecheck.txt | 0 .../functional/uninferable_all_object.py | 0 .../functional/unknown_encoding_jython.py | 0 .../functional/unknown_encoding_jython.rc | 0 .../functional/unknown_encoding_jython.txt | 0 .../functional/unknown_encoding_py29.py | 0 .../functional/unknown_encoding_py29.rc | 0 .../functional/unknown_encoding_py29.txt | 0 .../functional/unknown_encoding_pypy.py | 0 .../functional/unknown_encoding_pypy.rc | 0 .../functional/unknown_encoding_pypy.txt | 0 .../functional/unnecessary_lambda.py | 0 .../functional/unnecessary_lambda.txt | 0 .../functional/unnecessary_pass.py | 0 .../functional/unnecessary_pass.txt | 0 .../test => tests}/functional/unneeded_not.py | 0 .../functional/unneeded_not.txt | 0 .../functional/unpacked_exceptions.py | 0 .../functional/unpacked_exceptions.rc | 0 .../functional/unpacked_exceptions.txt | 0 .../test => tests}/functional/unpacking.py | 0 .../functional/unpacking_generalizations.py | 0 .../functional/unpacking_generalizations.rc | 0 .../functional/unpacking_generalizations.txt | 0 .../functional/unpacking_non_sequence.py | 0 .../functional/unpacking_non_sequence.txt | 0 .../test => tests}/functional/unreachable.py | 0 .../test => tests}/functional/unreachable.txt | 0 .../functional/unrecognized_inline_option.py | 0 .../functional/unrecognized_inline_option.txt | 0 .../functional/unsubscriptable_value.py | 0 .../functional/unsubscriptable_value.txt | 0 .../functional/unsubscriptable_value_py37.py | 0 .../functional/unsubscriptable_value_py37.rc | 0 .../functional/unsubscriptable_value_py37.txt | 0 .../unsupported_assignment_operation.py | 0 .../unsupported_assignment_operation.txt | 0 .../unsupported_binary_operation.py | 0 .../unsupported_binary_operation.rc | 0 .../unsupported_binary_operation.txt | 0 .../unsupported_delete_operation.py | 0 .../unsupported_delete_operation.txt | 0 .../functional/unused_argument.py | 0 .../functional/unused_argument.txt | 0 .../functional/unused_argument_py3.py | 0 .../functional/unused_argument_py3.rc | 0 .../functional/unused_argument_py3.txt | 0 .../functional/unused_global_variable1.py | 0 .../functional/unused_global_variable2.py | 0 .../functional/unused_global_variable2.rc | 0 .../functional/unused_global_variable2.txt | 0 .../functional/unused_global_variable3.py | 0 .../functional/unused_global_variable4.py | 0 .../functional/unused_global_variable4.rc | 0 .../functional/unused_global_variable4.txt | 0 .../functional/unused_import.py | 0 .../functional/unused_import.txt | 0 .../functional/unused_import_assigned_to.py | 0 .../functional/unused_typing_imports.py | 0 .../functional/unused_typing_imports.rc | 0 .../functional/unused_variable.py | 0 .../functional/unused_variable.txt | 0 .../functional/unused_variable_py36.py | 0 .../functional/unused_variable_py36.rc | 0 .../functional/unused_variable_py36.txt | 0 .../functional/used_before_assignment_488.py | 0 .../used_before_assignment_issue1081.py | 0 .../used_before_assignment_issue1081.txt | 0 .../used_before_assignment_issue853.py | 0 .../used_before_assignment_nonlocal.py | 0 .../used_before_assignment_nonlocal.rc | 0 .../used_before_assignment_nonlocal.txt | 0 .../used_prior_global_declaration.py | 0 .../used_prior_global_declaration.rc | 0 .../used_prior_global_declaration.txt | 0 .../functional/useless-import-alias.py | 0 .../functional/useless-import-alias.txt | 0 .../functional/useless_else_on_loop.py | 0 .../functional/useless_else_on_loop.txt | 0 .../functional/useless_object_inheritance.py | 0 .../functional/useless_object_inheritance.txt | 0 .../functional/useless_return.py | 0 .../functional/useless_return.txt | 0 .../functional/useless_super_delegation.py | 0 .../functional/useless_super_delegation.txt | 0 .../useless_super_delegation_py3.py | 0 .../useless_super_delegation_py3.rc | 0 .../useless_super_delegation_py3.txt | 0 .../useless_super_delegation_py35.py | 0 .../useless_super_delegation_py35.rc | 0 .../useless_super_delegation_py35.txt | 0 .../functional/using_constant_test.py | 0 .../functional/using_constant_test.txt | 0 .../functional/wildcard_import.py | 0 .../functional/wildcard_import.txt | 0 .../functional/wildcard_import_allowed.py | 0 .../functional/wildcard_import_allowed.rc | 0 .../functional/wildcard_import_allowed.txt | 0 .../functional/with_used_before_assign.py | 0 .../functional/with_used_before_assign.txt | 0 .../functional/with_using_generator.py | 0 .../functional/with_using_generator.txt | 0 .../functional/wrong_exception_operation.py | 0 .../functional/wrong_exception_operation.txt | 0 .../functional/wrong_import_order.py | 0 .../functional/wrong_import_order.txt | 0 .../functional/wrong_import_order2.py | 0 .../functional/wrong_import_position.py | 0 .../functional/wrong_import_position.txt | 0 .../functional/wrong_import_position10.py | 0 .../functional/wrong_import_position11.py | 0 .../functional/wrong_import_position11.txt | 0 .../functional/wrong_import_position12.py | 0 .../functional/wrong_import_position12.txt | 0 .../functional/wrong_import_position13.py | 0 .../functional/wrong_import_position13.txt | 0 .../functional/wrong_import_position14.py | 0 .../functional/wrong_import_position14.txt | 0 .../functional/wrong_import_position15.py | 0 .../functional/wrong_import_position2.py | 0 .../functional/wrong_import_position3.py | 0 .../functional/wrong_import_position4.py | 0 .../functional/wrong_import_position5.py | 0 .../functional/wrong_import_position6.py | 0 .../functional/wrong_import_position7.py | 0 .../functional/wrong_import_position8.py | 0 .../functional/wrong_import_position9.py | 0 ...ong_import_position_exclude_dunder_main.py | 0 ...ng_import_position_exclude_dunder_main.txt | 0 .../functional/yield_from_iterable_py33.py | 0 .../functional/yield_from_iterable_py33.rc | 0 .../functional/yield_from_iterable_py33.txt | 0 .../functional/yield_from_outside_func.py | 0 .../functional/yield_from_outside_func.rc | 0 .../functional/yield_from_outside_func.txt | 0 .../functional/yield_inside_async_function.py | 0 .../functional/yield_inside_async_function.rc | 0 .../yield_inside_async_function.txt | 0 .../yield_inside_async_function_py36.py | 0 .../yield_inside_async_function_py36.rc | 0 .../yield_inside_async_function_py36.txt | 0 .../functional/yield_outside_func.py | 0 .../functional/yield_outside_func.txt | 0 {pylint/test => tests}/input/__init__.py | 0 .../input/func_3k_removed_stuff_py_30.py | 0 .../input/func_bad_cont_dictcomp_py27.py | 0 .../test => tests}/input/func_bug113231.py | 0 .../input/func_disable_linebased.py | 0 .../input/func_dotted_ancestor.py | 0 {pylint/test => tests}/input/func_e0012.py | 0 {pylint/test => tests}/input/func_e0204.py | 0 {pylint/test => tests}/input/func_e12xx.py | 0 {pylint/test => tests}/input/func_e13xx.py | 0 .../input/func_excess_escapes.py | 0 .../test => tests}/input/func_first_arg.py | 0 {pylint/test => tests}/input/func_i0011.py | 0 {pylint/test => tests}/input/func_i0012.py | 0 {pylint/test => tests}/input/func_i0013.py | 0 {pylint/test => tests}/input/func_i0014.py | 0 {pylint/test => tests}/input/func_i0020.py | 0 {pylint/test => tests}/input/func_i0022.py | 0 .../func_logging_not_lazy_with_logger.py | 0 .../input/func_loopvar_in_dict_comp_py27.py | 0 .../input/func_module___dict__.py | 0 .../func_nameerror_on_string_substitution.py | 0 .../input/func_no_dummy_redefined.py | 0 ...ror___init___return_from_inner_function.py | 0 ...r_access_attr_before_def_false_positive.py | 0 .../input/func_noerror_base_init_vars.py | 0 .../input/func_noerror_builtin_module_test.py | 0 .../input/func_noerror_class_attributes.py | 0 ...oerror_classes_meth_could_be_a_function.py | 0 ...noerror_classes_protected_member_access.py | 0 .../input/func_noerror_decorator_scope.py | 0 ...noerror_e1101_9588_base_attr_aug_assign.py | 0 ...func_noerror_external_classmethod_crash.py | 0 .../input/func_noerror_inner_classes.py | 0 .../func_noerror_lambda_use_before_assign.py | 0 .../input/func_noerror_mcs_attr_access.py | 0 .../func_noerror_new_style_class_py_30.py | 0 .../func_noerror_no_warning_docstring.py | 0 .../func_noerror_object_as_class_attribute.py | 0 .../input/func_noerror_overloaded_operator.py | 0 .../func_noerror_property_affectation_py26.py | 0 .../input/func_noerror_yield_assign_py25.py | 0 .../input/func_noerror_yield_return_mix.py | 0 .../input/func_nonregr___file___global.py | 0 .../input/func_return_yield_mix_py_33.py | 0 .../func_typecheck_callfunc_assigment.py | 0 .../input/func_unused_import_py30.py | 0 ...riables_unused_name_from_wilcard_import.py | 0 .../test => tests}/input/func_w0122_py_30.py | 0 {pylint/test => tests}/input/func_w0233.py | 0 .../test => tests}/input/func_w0332_py_30.py | 0 {pylint/test => tests}/input/func_w0401.py | 0 .../input/func_w0401_disabled.py | 0 .../input/func_w0401_disabled_in_func.py | 0 .../input/func_w0401_package/__init__.py | 0 .../func_w0401_package/all_the_things.py | 0 .../input/func_w0401_package/thing1.py | 0 .../input/func_w0401_package/thing2.py | 0 {pylint/test => tests}/input/func_w0404.py | 0 {pylint/test => tests}/input/func_w0405.py | 0 {pylint/test => tests}/input/func_w0406.py | 0 {pylint/test => tests}/input/func_w0611.py | 0 {pylint/test => tests}/input/func_w0612.py | 0 {pylint/test => tests}/input/func_w0613.py | 0 .../test => tests}/input/func_w0623_py30.py | 0 {pylint/test => tests}/input/func_w0801.py | 0 .../input/hide_code_with_imports.py | 0 .../input/ignore_except_pass_by_default.py | 0 {pylint/test => tests}/input/multiline-import | 0 {pylint/test => tests}/input/noext | 0 {pylint/test => tests}/input/not__init__.py | 0 {pylint/test => tests}/input/similar1 | 0 {pylint/test => tests}/input/similar2 | 0 {pylint/test => tests}/input/w0401_cycle.py | 0 {pylint/test => tests}/input/w0801_same.py | 0 .../message/unittest_message.py | 0 .../message/unittest_message_store.py | 0 .../messages/builtin_module.txt | 0 .../messages/func_3k_removed_stuff_py_30.txt | 0 .../messages/func_bad_cont_dictcomp_py27.txt | 0 .../messages/func_bug113231.txt | 0 .../messages/func_disable_linebased.txt | 0 .../messages/func_disable_linebased_py30.txt | 0 .../messages/func_dotted_ancestor.txt | 0 .../test => tests}/messages/func_e0012.txt | 0 .../test => tests}/messages/func_e0204.txt | 0 .../test => tests}/messages/func_e12xx.txt | 0 .../test => tests}/messages/func_e13xx.txt | 0 .../messages/func_e13xx_py30.txt | 0 .../messages/func_excess_escapes.txt | 0 .../messages/func_first_arg.txt | 0 .../test => tests}/messages/func_i0011.txt | 0 .../test => tests}/messages/func_i0012.txt | 0 .../test => tests}/messages/func_i0013.txt | 0 .../test => tests}/messages/func_i0014.txt | 0 .../test => tests}/messages/func_i0020.txt | 0 .../test => tests}/messages/func_i0022.txt | 0 .../func_logging_not_lazy_with_logger.txt | 0 .../func_loopvar_in_dict_comp_py27.txt | 0 .../messages/func_module___dict__.txt | 0 .../func_nameerror_on_string_substitution.txt | 0 .../messages/func_no_dummy_redefined.txt | 0 .../messages/func_nonregr___file___global.txt | 0 .../messages/func_raw_escapes.txt | 0 .../messages/func_return_yield_mix_py_33.txt | 0 .../messages/func_toolonglines_py30.txt | 0 .../func_typecheck_callfunc_assigment.txt | 0 .../messages/func_typecheck_getattr_py30.txt | 0 .../func_typecheck_non_callable_call.txt | 0 .../messages/func_unicode_literal_py26.txt | 0 .../messages/func_unicode_literal_py274.txt | 0 .../messages/func_unused_import_py30.txt | 0 .../func_use_for_or_listcomp_var_py29.txt | 0 .../func_use_for_or_listcomp_var_py30.txt | 0 ...iables_unused_name_from_wilcard_import.txt | 0 .../messages/func_w0122_py_30.txt | 0 .../test => tests}/messages/func_w0233.txt | 0 .../test => tests}/messages/func_w0312.txt | 0 .../messages/func_w0332_py_30.txt | 0 .../test => tests}/messages/func_w0401.txt | 0 .../messages/func_w0401_disabled.txt | 0 .../messages/func_w0401_disabled_in_func.txt | 0 .../messages/func_w0401_package.txt | 0 .../test => tests}/messages/func_w0404.txt | 0 .../test => tests}/messages/func_w0405.txt | 0 .../test => tests}/messages/func_w0406.txt | 0 .../test => tests}/messages/func_w0611.txt | 0 .../test => tests}/messages/func_w0612.txt | 0 .../test => tests}/messages/func_w0613.txt | 0 .../test => tests}/messages/func_w0622.txt | 0 .../test => tests}/messages/func_w0623.txt | 0 .../messages/func_w0623_py30.txt | 0 .../messages/func_w0623_py_30.txt | 0 .../test => tests}/messages/func_w0801.txt | 0 .../messages/func_with_without_as_py25.txt | 0 .../test => tests}/regrtest_data/.pylintrc | 0 .../regrtest_data/absimp/__init__.py | 0 .../regrtest_data/absimp/string.py | 0 .../regrtest_data/application_crash.py | 0 .../regrtest_data/bad_package/__init__.py | 0 .../regrtest_data/bad_package/wrong.py | 0 .../regrtest_data/beyond_top/__init__.py | 0 .../regrtest_data/beyond_top/data.py | 0 .../regrtest_data/classdoc_usage.py | 0 .../regrtest_data/comments_pylintrc | 0 .../regrtest_data/decimal_inference.py | 0 .../regrtest_data/descriptor_crash.py | 0 .../regrtest_data/dummy/__init__.py | 0 .../regrtest_data/dummy/another.py | 0 .../regrtest_data/dummy/dummy.py | 0 .../regrtest_data/dummy_plugin.rc | 0 .../dummy_plugin/dummy_conf_plugin.py | 0 .../dummy_plugin/dummy_plugin.py | 0 {pylint/test => tests}/regrtest_data/empty.py | 0 .../regrtest_data/func_block_disable_msg.py | 0 .../regrtest_data/import_assign.py | 0 .../import_package_subpackage_module.py | 0 .../regrtest_data/import_something.py | 0 .../regrtest_data/init_wildcard/__init__.py | 0 {pylint/test => tests}/regrtest_data/meta.py | 0 .../regrtest_data/module_global.py | 0 .../regrtest_data/no_stdout_encoding.py | 0 .../regrtest_data/numarray_import.py | 0 .../regrtest_data/numarray_inf.py | 0 .../regrtest_data/package/AudioTime.py | 0 .../regrtest_data/package/__init__.py | 0 .../package/subpackage/__init__.py | 0 .../package/subpackage/module.py | 0 .../regrtest_data/package_all/__init__.py | 0 .../regrtest_data/package_all/notmissing.py | 0 .../regrtest_data/precedence_test.py | 0 .../regrtest_data/py3k-disabled.rc | 0 .../regrtest_data/py3k_error_flag.py | 0 .../regrtest_data/py3k_errors_and_warnings.py | 0 .../special_attr_scope_lookup_crash.py | 0 .../regrtest_data/syntax_error.py | 0 .../regrtest_data/test_pylintrc_comments.py | 0 .../try_finally_disable_msg_crash.py | 0 .../regrtest_data/unused_variable.py | 0 .../test => tests}/regrtest_data/wildcard.py | 0 .../regrtest_data/wrong_import_position.py | 0 {pylint/test => tests}/test_func.py | 0 {pylint/test => tests}/test_functional.py | 0 {pylint/test => tests}/test_import_graph.py | 0 {pylint/test => tests}/test_regr.py | 0 {pylint/test => tests}/test_self.py | 40 +++++++++---------- .../test => tests}/unittest_checker_base.py | 0 .../unittest_checker_classes.py | 0 .../unittest_checker_exceptions.py | 0 .../test => tests}/unittest_checker_format.py | 0 .../unittest_checker_imports.py | 0 .../unittest_checker_logging.py | 0 .../test => tests}/unittest_checker_misc.py | 0 .../unittest_checker_python3.py | 0 .../unittest_checker_similar.py | 0 .../unittest_checker_spelling.py | 0 .../test => tests}/unittest_checker_stdlib.py | 0 .../unittest_checker_strings.py | 0 .../unittest_checker_typecheck.py | 0 .../unittest_checker_variables.py | 0 .../test => tests}/unittest_checkers_utils.py | 0 {pylint/test => tests}/unittest_config.py | 0 {pylint/test => tests}/unittest_lint.py | 0 .../unittest_pyreverse_diadefs.py | 0 .../unittest_pyreverse_inspector.py | 2 +- .../unittest_pyreverse_writer.py | 0 .../test => tests}/unittest_reporters_json.py | 0 {pylint/test => tests}/unittest_reporting.py | 0 .../utils/unittest_ast_walker.py | 0 .../test => tests}/utils/unittest_utils.py | 0 tox.ini | 6 +-- 1068 files changed, 36 insertions(+), 60 deletions(-) rename {pylint/test => tests}/acceptance/test_stdlib.py (100%) rename {pylint/test => tests}/conftest.py (100%) rename {pylint/test => tests}/data/__init__.py (100%) rename {pylint/test => tests}/data/ascript (100%) rename {pylint/test => tests}/data/classes_No_Name.dot (100%) rename {pylint/test => tests}/data/clientmodule_test.py (100%) rename {pylint/test => tests}/data/packages_No_Name.dot (100%) rename {pylint/test => tests}/data/suppliermodule_test.py (100%) rename {pylint/test => tests}/extensions/__init__.py (100%) rename {pylint/test => tests}/extensions/data/bad_builtin.py (100%) rename {pylint/test => tests}/extensions/data/broad_try_clause.py (100%) rename {pylint/test => tests}/extensions/data/compare_to_zero.py (100%) rename {pylint/test => tests}/extensions/data/docstring.py (100%) rename {pylint/test => tests}/extensions/data/elif.py (100%) rename {pylint/test => tests}/extensions/data/empty_string_comparison.py (100%) rename {pylint/test => tests}/extensions/data/mccabe.py (100%) rename {pylint/test => tests}/extensions/data/overlapping_exceptions.py (100%) rename {pylint/test => tests}/extensions/data/overlapping_exceptions_py33.py (100%) rename {pylint/test => tests}/extensions/data/redefined.py (100%) rename {pylint/test => tests}/extensions/test_bad_builtin.py (100%) rename {pylint/test => tests}/extensions/test_broad_try_clause.py (100%) rename {pylint/test => tests}/extensions/test_check_docs.py (100%) rename {pylint/test => tests}/extensions/test_check_docs_utils.py (100%) rename {pylint/test => tests}/extensions/test_check_mccabe.py (100%) rename {pylint/test => tests}/extensions/test_check_raise_docs.py (100%) rename {pylint/test => tests}/extensions/test_check_return_docs.py (100%) rename {pylint/test => tests}/extensions/test_check_yields_docs.py (100%) rename {pylint/test => tests}/extensions/test_comparetozero.py (100%) rename {pylint/test => tests}/extensions/test_docstyle.py (100%) rename {pylint/test => tests}/extensions/test_elseif_used.py (100%) rename {pylint/test => tests}/extensions/test_emptystring.py (100%) rename {pylint/test => tests}/extensions/test_overlapping_exceptions.py (100%) rename {pylint/test => tests}/extensions/test_redefined.py (100%) rename {pylint/test => tests}/functional/__init__.py (100%) rename {pylint/test => tests}/functional/abstract_abc_methods.py (100%) rename {pylint/test => tests}/functional/abstract_class_instantiated_in_class.py (100%) rename {pylint/test => tests}/functional/abstract_class_instantiated_py2.py (100%) rename {pylint/test => tests}/functional/abstract_class_instantiated_py2.rc (100%) rename {pylint/test => tests}/functional/abstract_class_instantiated_py2.txt (100%) rename {pylint/test => tests}/functional/abstract_class_instantiated_py3.py (100%) rename {pylint/test => tests}/functional/abstract_class_instantiated_py3.rc (100%) rename {pylint/test => tests}/functional/abstract_class_instantiated_py3.txt (100%) rename {pylint/test => tests}/functional/abstract_class_instantiated_py34.py (100%) rename {pylint/test => tests}/functional/abstract_class_instantiated_py34.rc (100%) rename {pylint/test => tests}/functional/abstract_class_instantiated_py34.txt (100%) rename {pylint/test => tests}/functional/abstract_method_py2.py (100%) rename {pylint/test => tests}/functional/abstract_method_py2.rc (100%) rename {pylint/test => tests}/functional/abstract_method_py2.txt (100%) rename {pylint/test => tests}/functional/abstract_method_py3.py (100%) rename {pylint/test => tests}/functional/abstract_method_py3.rc (100%) rename {pylint/test => tests}/functional/abstract_method_py3.txt (100%) rename {pylint/test => tests}/functional/access_member_before_definition.py (100%) rename {pylint/test => tests}/functional/access_member_before_definition.txt (100%) rename {pylint/test => tests}/functional/access_to__name__.py (100%) rename {pylint/test => tests}/functional/access_to__name__.txt (100%) rename {pylint/test => tests}/functional/access_to_protected_members.py (100%) rename {pylint/test => tests}/functional/access_to_protected_members.txt (100%) rename {pylint/test => tests}/functional/anomalous_unicode_escape_py2.py (100%) rename {pylint/test => tests}/functional/anomalous_unicode_escape_py2.rc (100%) rename {pylint/test => tests}/functional/anomalous_unicode_escape_py2.txt (100%) rename {pylint/test => tests}/functional/anomalous_unicode_escape_py3.py (100%) rename {pylint/test => tests}/functional/anomalous_unicode_escape_py3.rc (100%) rename {pylint/test => tests}/functional/anomalous_unicode_escape_py3.txt (100%) rename {pylint/test => tests}/functional/arguments.py (100%) rename {pylint/test => tests}/functional/arguments.rc (100%) rename {pylint/test => tests}/functional/arguments.txt (100%) rename {pylint/test => tests}/functional/arguments_differ.py (100%) rename {pylint/test => tests}/functional/arguments_differ.txt (100%) rename {pylint/test => tests}/functional/arguments_differ_py3.py (100%) rename {pylint/test => tests}/functional/arguments_differ_py3.rc (100%) rename {pylint/test => tests}/functional/arguments_differ_py3.txt (100%) rename {pylint/test => tests}/functional/assert_on_tuple.py (100%) rename {pylint/test => tests}/functional/assert_on_tuple.txt (100%) rename {pylint/test => tests}/functional/assigning_non_slot.py (100%) rename {pylint/test => tests}/functional/assigning_non_slot.txt (100%) rename {pylint/test => tests}/functional/assignment_from_no_return.py (100%) rename {pylint/test => tests}/functional/assignment_from_no_return.txt (100%) rename {pylint/test => tests}/functional/assignment_from_no_return_py3.py (100%) rename {pylint/test => tests}/functional/assignment_from_no_return_py3.rc (100%) rename {pylint/test => tests}/functional/assignment_from_no_return_py3.txt (100%) rename {pylint/test => tests}/functional/async_functions.py (100%) rename {pylint/test => tests}/functional/async_functions.rc (100%) rename {pylint/test => tests}/functional/async_functions.txt (100%) rename {pylint/test => tests}/functional/attribute_defined_outside_init.py (100%) rename {pylint/test => tests}/functional/attribute_defined_outside_init.txt (100%) rename {pylint/test => tests}/functional/bad_continuation.py (100%) rename {pylint/test => tests}/functional/bad_continuation.txt (100%) rename {pylint/test => tests}/functional/bad_continuation_py36.py (100%) rename {pylint/test => tests}/functional/bad_continuation_py36.rc (100%) rename {pylint/test => tests}/functional/bad_continuation_tabs.py (100%) rename {pylint/test => tests}/functional/bad_continuation_tabs.rc (100%) rename {pylint/test => tests}/functional/bad_continuation_tabs.txt (100%) rename {pylint/test => tests}/functional/bad_except_order.py (100%) rename {pylint/test => tests}/functional/bad_except_order.txt (100%) rename {pylint/test => tests}/functional/bad_exception_context.py (100%) rename {pylint/test => tests}/functional/bad_exception_context.rc (100%) rename {pylint/test => tests}/functional/bad_exception_context.txt (100%) rename {pylint/test => tests}/functional/bad_indentation.py (100%) rename {pylint/test => tests}/functional/bad_indentation.txt (100%) rename {pylint/test => tests}/functional/bad_inline_option.py (100%) rename {pylint/test => tests}/functional/bad_inline_option.rc (100%) rename {pylint/test => tests}/functional/bad_inline_option.txt (100%) rename {pylint/test => tests}/functional/bad_open_mode.py (100%) rename {pylint/test => tests}/functional/bad_open_mode.rc (100%) rename {pylint/test => tests}/functional/bad_open_mode.txt (100%) rename {pylint/test => tests}/functional/bad_open_mode_py3.py (100%) rename {pylint/test => tests}/functional/bad_open_mode_py3.rc (100%) rename {pylint/test => tests}/functional/bad_open_mode_py3.txt (100%) rename {pylint/test => tests}/functional/bad_reversed_sequence.py (100%) rename {pylint/test => tests}/functional/bad_reversed_sequence.txt (100%) rename {pylint/test => tests}/functional/bad_staticmethod_argument.py (100%) rename {pylint/test => tests}/functional/bad_staticmethod_argument.txt (100%) rename {pylint/test => tests}/functional/bad_thread_instantiation.py (100%) rename {pylint/test => tests}/functional/bad_thread_instantiation.txt (100%) rename {pylint/test => tests}/functional/bad_whitespace.py (100%) rename {pylint/test => tests}/functional/bad_whitespace.txt (100%) rename {pylint/test => tests}/functional/bare_except.py (100%) rename {pylint/test => tests}/functional/bare_except.txt (100%) rename {pylint/test => tests}/functional/blacklisted_name.py (100%) rename {pylint/test => tests}/functional/blacklisted_name.txt (100%) rename {pylint/test => tests}/functional/boolean_datetime.py (100%) rename {pylint/test => tests}/functional/boolean_datetime.rc (100%) rename {pylint/test => tests}/functional/boolean_datetime.txt (100%) rename {pylint/test => tests}/functional/broad_except.py (100%) rename {pylint/test => tests}/functional/broad_except.txt (100%) rename {pylint/test => tests}/functional/bugfix_local_scope_metaclass_1177.py (100%) rename {pylint/test => tests}/functional/bugfix_local_scope_metaclass_1177.rc (100%) rename {pylint/test => tests}/functional/cellvar_escaping_loop.py (100%) rename {pylint/test => tests}/functional/cellvar_escaping_loop.txt (100%) rename {pylint/test => tests}/functional/class_members_py27.py (100%) rename {pylint/test => tests}/functional/class_members_py27.rc (100%) rename {pylint/test => tests}/functional/class_members_py27.txt (100%) rename {pylint/test => tests}/functional/class_members_py30.py (100%) rename {pylint/test => tests}/functional/class_members_py30.rc (100%) rename {pylint/test => tests}/functional/class_members_py30.txt (100%) rename {pylint/test => tests}/functional/class_scope.py (100%) rename {pylint/test => tests}/functional/class_scope.txt (100%) rename {pylint/test => tests}/functional/comparison_with_callable.py (100%) rename {pylint/test => tests}/functional/comparison_with_callable.txt (100%) rename {pylint/test => tests}/functional/confidence_filter.py (100%) rename {pylint/test => tests}/functional/confidence_filter.rc (100%) rename {pylint/test => tests}/functional/confidence_filter.txt (100%) rename {pylint/test => tests}/functional/confusing_with_statement.py (100%) rename {pylint/test => tests}/functional/confusing_with_statement.txt (100%) rename {pylint/test => tests}/functional/consider_iterating_dictionary.py (100%) rename {pylint/test => tests}/functional/consider_iterating_dictionary.txt (100%) rename {pylint/test => tests}/functional/consider_join.py (100%) rename {pylint/test => tests}/functional/consider_join.txt (100%) rename {pylint/test => tests}/functional/consider_merging_isinstance.py (100%) rename {pylint/test => tests}/functional/consider_merging_isinstance.txt (100%) rename {pylint/test => tests}/functional/consider_swap_variables.py (100%) rename {pylint/test => tests}/functional/consider_swap_variables.txt (100%) rename {pylint/test => tests}/functional/consider_using_dict_comprehension.py (100%) rename {pylint/test => tests}/functional/consider_using_dict_comprehension.txt (100%) rename {pylint/test => tests}/functional/consider_using_enumerate.py (100%) rename {pylint/test => tests}/functional/consider_using_enumerate.txt (100%) rename {pylint/test => tests}/functional/consider_using_get.py (100%) rename {pylint/test => tests}/functional/consider_using_get.txt (100%) rename {pylint/test => tests}/functional/consider_using_in.py (100%) rename {pylint/test => tests}/functional/consider_using_in.txt (100%) rename {pylint/test => tests}/functional/consider_using_set_comprehension.py (100%) rename {pylint/test => tests}/functional/consider_using_set_comprehension.txt (100%) rename {pylint/test => tests}/functional/continue_in_finally.py (100%) rename {pylint/test => tests}/functional/continue_in_finally.txt (100%) rename {pylint/test => tests}/functional/control_pragmas.py (100%) rename {pylint/test => tests}/functional/control_pragmas.txt (100%) rename {pylint/test => tests}/functional/crash_missing_module_type.py (100%) rename {pylint/test => tests}/functional/crash_missing_module_type.txt (100%) rename {pylint/test => tests}/functional/ctor_arguments.py (100%) rename {pylint/test => tests}/functional/ctor_arguments.txt (100%) rename {pylint/test => tests}/functional/dangerous_default_value.py (100%) rename {pylint/test => tests}/functional/dangerous_default_value.rc (100%) rename {pylint/test => tests}/functional/dangerous_default_value.txt (100%) rename {pylint/test => tests}/functional/dangerous_default_value_py30.py (100%) rename {pylint/test => tests}/functional/dangerous_default_value_py30.rc (100%) rename {pylint/test => tests}/functional/dangerous_default_value_py30.txt (100%) rename {pylint/test => tests}/functional/defined_and_used_on_same_line.py (100%) rename {pylint/test => tests}/functional/deprecated_lambda.py (100%) rename {pylint/test => tests}/functional/deprecated_lambda.rc (100%) rename {pylint/test => tests}/functional/deprecated_lambda.txt (100%) rename {pylint/test => tests}/functional/deprecated_method_getmoduleinfo.py (100%) rename {pylint/test => tests}/functional/deprecated_method_getmoduleinfo.rc (100%) rename {pylint/test => tests}/functional/deprecated_method_getmoduleinfo.txt (100%) rename {pylint/test => tests}/functional/deprecated_methods_py2.py (100%) rename {pylint/test => tests}/functional/deprecated_methods_py2.rc (100%) rename {pylint/test => tests}/functional/deprecated_methods_py2.txt (100%) rename {pylint/test => tests}/functional/deprecated_methods_py3.py (100%) rename {pylint/test => tests}/functional/deprecated_methods_py3.rc (100%) rename {pylint/test => tests}/functional/deprecated_methods_py3.txt (100%) rename {pylint/test => tests}/functional/deprecated_methods_py36.py (100%) rename {pylint/test => tests}/functional/deprecated_methods_py36.rc (100%) rename {pylint/test => tests}/functional/deprecated_methods_py36.txt (100%) rename {pylint/test => tests}/functional/deprecated_methods_py38.py (100%) rename {pylint/test => tests}/functional/deprecated_methods_py38.rc (100%) rename {pylint/test => tests}/functional/deprecated_methods_py38.txt (100%) rename {pylint/test => tests}/functional/deprecated_module_py2.py (100%) rename {pylint/test => tests}/functional/deprecated_module_py2.rc (100%) rename {pylint/test => tests}/functional/deprecated_module_py2.txt (100%) rename {pylint/test => tests}/functional/deprecated_module_py3.py (100%) rename {pylint/test => tests}/functional/deprecated_module_py3.rc (100%) rename {pylint/test => tests}/functional/deprecated_module_py3.txt (100%) rename {pylint/test => tests}/functional/deprecated_module_py36.py (100%) rename {pylint/test => tests}/functional/deprecated_module_py36.rc (100%) rename {pylint/test => tests}/functional/deprecated_module_py36.txt (100%) rename {pylint/test => tests}/functional/deprecated_module_py4.py (100%) rename {pylint/test => tests}/functional/deprecated_module_py4.rc (100%) rename {pylint/test => tests}/functional/deprecated_module_py4.txt (100%) rename {pylint/test => tests}/functional/deprecated_module_uninstalled.py (100%) rename {pylint/test => tests}/functional/deprecated_module_uninstalled.rc (100%) rename {pylint/test => tests}/functional/deprecated_module_uninstalled.txt (100%) rename {pylint/test => tests}/functional/dict_iter_missing_items.py (100%) rename {pylint/test => tests}/functional/dict_iter_missing_items.txt (100%) rename {pylint/test => tests}/functional/disable_msg_github_issue_1389.py (100%) rename {pylint/test => tests}/functional/disable_msg_github_issue_1389.rc (100%) rename {pylint/test => tests}/functional/disable_ungrouped_imports.py (100%) rename {pylint/test => tests}/functional/disable_ungrouped_imports.txt (100%) rename {pylint/test => tests}/functional/disable_wrong_import_order.py (100%) rename {pylint/test => tests}/functional/disable_wrong_import_order.txt (100%) rename {pylint/test => tests}/functional/disable_wrong_import_position.py (100%) rename {pylint/test => tests}/functional/docstrings.py (100%) rename {pylint/test => tests}/functional/docstrings.txt (100%) rename {pylint/test => tests}/functional/duplicate_argument_name.py (100%) rename {pylint/test => tests}/functional/duplicate_argument_name.txt (100%) rename {pylint/test => tests}/functional/duplicate_argument_name_py3.py (100%) rename {pylint/test => tests}/functional/duplicate_argument_name_py3.rc (100%) rename {pylint/test => tests}/functional/duplicate_argument_name_py3.txt (100%) rename {pylint/test => tests}/functional/duplicate_bases.py (100%) rename {pylint/test => tests}/functional/duplicate_bases.txt (100%) rename {pylint/test => tests}/functional/duplicate_dict_literal_key.py (100%) rename {pylint/test => tests}/functional/duplicate_dict_literal_key.txt (100%) rename {pylint/test => tests}/functional/duplicate_except.py (100%) rename {pylint/test => tests}/functional/duplicate_except.txt (100%) rename {pylint/test => tests}/functional/duplicate_string_formatting_argument.py (100%) rename {pylint/test => tests}/functional/duplicate_string_formatting_argument.txt (100%) rename {pylint/test => tests}/functional/eval_used.py (100%) rename {pylint/test => tests}/functional/eval_used.txt (100%) rename {pylint/test => tests}/functional/exception_is_binary_op.py (100%) rename {pylint/test => tests}/functional/exception_is_binary_op.txt (100%) rename {pylint/test => tests}/functional/exception_message.py (100%) rename {pylint/test => tests}/functional/exception_message.rc (100%) rename {pylint/test => tests}/functional/exception_message.txt (100%) rename {pylint/test => tests}/functional/exec_used_py2.py (100%) rename {pylint/test => tests}/functional/exec_used_py2.rc (100%) rename {pylint/test => tests}/functional/exec_used_py2.txt (100%) rename {pylint/test => tests}/functional/exec_used_py3.py (100%) rename {pylint/test => tests}/functional/exec_used_py3.rc (100%) rename {pylint/test => tests}/functional/exec_used_py3.txt (100%) rename {pylint/test => tests}/functional/fallback_import_disabled.py (100%) rename {pylint/test => tests}/functional/fallback_import_disabled.txt (100%) rename {pylint/test => tests}/functional/fallback_import_enabled.py (100%) rename {pylint/test => tests}/functional/fallback_import_enabled.rc (100%) rename {pylint/test => tests}/functional/fallback_import_enabled.txt (100%) rename {pylint/test => tests}/functional/fixme.py (100%) rename {pylint/test => tests}/functional/fixme.txt (100%) rename {pylint/test => tests}/functional/fixme_bad_formatting_1139.py (100%) rename {pylint/test => tests}/functional/fixme_bad_formatting_1139.rc (100%) rename {pylint/test => tests}/functional/fixme_bad_formatting_1139.txt (100%) rename {pylint/test => tests}/functional/formatted_string_literal_with_if_py36.py (100%) rename {pylint/test => tests}/functional/formatted_string_literal_with_if_py36.rc (100%) rename {pylint/test => tests}/functional/formatting.txt (100%) rename {pylint/test => tests}/functional/function_redefined.py (100%) rename {pylint/test => tests}/functional/function_redefined.txt (100%) rename {pylint/test => tests}/functional/future_import.py (100%) rename {pylint/test => tests}/functional/future_unicode_literals.py (100%) rename {pylint/test => tests}/functional/future_unicode_literals.rc (100%) rename {pylint/test => tests}/functional/future_unicode_literals.txt (100%) rename {pylint/test => tests}/functional/generated_members.py (100%) rename {pylint/test => tests}/functional/generated_members.rc (100%) rename {pylint/test => tests}/functional/genexp_in_class_scope.py (100%) rename {pylint/test => tests}/functional/genexp_in_class_scope.txt (100%) rename {pylint/test => tests}/functional/genexpr_variable_scope.py (100%) rename {pylint/test => tests}/functional/genexpr_variable_scope.txt (100%) rename {pylint/test => tests}/functional/globals.py (100%) rename {pylint/test => tests}/functional/globals.txt (100%) rename {pylint/test => tests}/functional/implicit_str_concat_in_sequence.py (100%) rename {pylint/test => tests}/functional/implicit_str_concat_in_sequence.txt (100%) rename {pylint/test => tests}/functional/implicit_str_concat_in_sequence_latin1.py (100%) rename {pylint/test => tests}/functional/implicit_str_concat_in_sequence_latin1.txt (100%) rename {pylint/test => tests}/functional/implicit_str_concat_in_sequence_multiline.py (100%) rename {pylint/test => tests}/functional/implicit_str_concat_in_sequence_multiline.rc (100%) rename {pylint/test => tests}/functional/implicit_str_concat_in_sequence_multiline.txt (100%) rename {pylint/test => tests}/functional/implicit_str_concat_in_sequence_utf8.py (100%) rename {pylint/test => tests}/functional/implicit_str_concat_in_sequence_utf8.txt (100%) rename {pylint/test => tests}/functional/import_error.py (100%) rename {pylint/test => tests}/functional/import_error.rc (100%) rename {pylint/test => tests}/functional/import_error.txt (100%) rename {pylint/test => tests}/functional/inconsistent_mro.py (100%) rename {pylint/test => tests}/functional/inconsistent_mro.txt (100%) rename {pylint/test => tests}/functional/inconsistent_returns.py (100%) rename {pylint/test => tests}/functional/inconsistent_returns.rc (100%) rename {pylint/test => tests}/functional/inconsistent_returns.txt (100%) rename {pylint/test => tests}/functional/indexing_exception.py (100%) rename {pylint/test => tests}/functional/indexing_exception.rc (100%) rename {pylint/test => tests}/functional/indexing_exception.txt (100%) rename {pylint/test => tests}/functional/inherit_non_class.py (100%) rename {pylint/test => tests}/functional/inherit_non_class.txt (100%) rename {pylint/test => tests}/functional/init_is_generator.py (100%) rename {pylint/test => tests}/functional/init_is_generator.txt (100%) rename {pylint/test => tests}/functional/init_not_called.py (100%) rename {pylint/test => tests}/functional/init_not_called.txt (100%) rename {pylint/test => tests}/functional/init_subclass_classmethod_py36.py (100%) rename {pylint/test => tests}/functional/init_subclass_classmethod_py36.rc (100%) rename {pylint/test => tests}/functional/invalid_all_object.py (100%) rename {pylint/test => tests}/functional/invalid_all_object.txt (100%) rename {pylint/test => tests}/functional/invalid_encoded_data.py (100%) rename {pylint/test => tests}/functional/invalid_encoded_data.rc (100%) rename {pylint/test => tests}/functional/invalid_encoded_data.txt (100%) rename {pylint/test => tests}/functional/invalid_encoding_py27.py (100%) rename {pylint/test => tests}/functional/invalid_encoding_py27.rc (100%) rename {pylint/test => tests}/functional/invalid_encoding_py27.txt (100%) rename {pylint/test => tests}/functional/invalid_envvar_value.py (100%) rename {pylint/test => tests}/functional/invalid_envvar_value.txt (100%) rename {pylint/test => tests}/functional/invalid_exceptions_caught.py (100%) rename {pylint/test => tests}/functional/invalid_exceptions_caught.txt (100%) rename {pylint/test => tests}/functional/invalid_exceptions_raised.py (100%) rename {pylint/test => tests}/functional/invalid_exceptions_raised.txt (100%) rename {pylint/test => tests}/functional/invalid_length_returned.py (100%) rename {pylint/test => tests}/functional/invalid_length_returned.txt (100%) rename {pylint/test => tests}/functional/invalid_metaclass.py (100%) rename {pylint/test => tests}/functional/invalid_metaclass.txt (100%) rename {pylint/test => tests}/functional/invalid_metaclass_py3.py (100%) rename {pylint/test => tests}/functional/invalid_metaclass_py3.rc (100%) rename {pylint/test => tests}/functional/invalid_metaclass_py3.txt (100%) rename {pylint/test => tests}/functional/invalid_name.py (100%) rename {pylint/test => tests}/functional/invalid_name.txt (100%) rename {pylint/test => tests}/functional/invalid_sequence_index.py (100%) rename {pylint/test => tests}/functional/invalid_sequence_index.txt (100%) rename {pylint/test => tests}/functional/invalid_slice_index.py (100%) rename {pylint/test => tests}/functional/invalid_slice_index.txt (100%) rename {pylint/test => tests}/functional/invalid_star_assignment_target.py (100%) rename {pylint/test => tests}/functional/invalid_star_assignment_target.rc (100%) rename {pylint/test => tests}/functional/invalid_star_assignment_target.txt (100%) rename {pylint/test => tests}/functional/invalid_unary_operand_type.py (100%) rename {pylint/test => tests}/functional/invalid_unary_operand_type.txt (100%) rename {pylint/test => tests}/functional/iterable_context.py (100%) rename {pylint/test => tests}/functional/iterable_context.txt (100%) rename {pylint/test => tests}/functional/iterable_context_py2.py (100%) rename {pylint/test => tests}/functional/iterable_context_py2.rc (100%) rename {pylint/test => tests}/functional/iterable_context_py2.txt (100%) rename {pylint/test => tests}/functional/iterable_context_py3.py (100%) rename {pylint/test => tests}/functional/iterable_context_py3.rc (100%) rename {pylint/test => tests}/functional/iterable_context_py3.txt (100%) rename {pylint/test => tests}/functional/iterable_context_py36.py (100%) rename {pylint/test => tests}/functional/iterable_context_py36.rc (100%) rename {pylint/test => tests}/functional/iterable_context_py36.txt (100%) rename {pylint/test => tests}/functional/keyword_arg_before_vararg.py (100%) rename {pylint/test => tests}/functional/keyword_arg_before_vararg.txt (100%) rename {pylint/test => tests}/functional/len_checks.py (100%) rename {pylint/test => tests}/functional/len_checks.txt (100%) rename {pylint/test => tests}/functional/line_endings.py (100%) rename {pylint/test => tests}/functional/line_endings.rc (100%) rename {pylint/test => tests}/functional/line_endings.txt (100%) rename {pylint/test => tests}/functional/line_too_long.py (100%) rename {pylint/test => tests}/functional/line_too_long.txt (100%) rename {pylint/test => tests}/functional/line_too_long_end_of_module.py (100%) rename {pylint/test => tests}/functional/literal_comparison.py (100%) rename {pylint/test => tests}/functional/literal_comparison.txt (100%) rename {pylint/test => tests}/functional/logging_format_interpolation.py (100%) rename {pylint/test => tests}/functional/logging_format_interpolation.txt (100%) rename {pylint/test => tests}/functional/logging_format_interpolation_py36.py (100%) rename {pylint/test => tests}/functional/logging_format_interpolation_py36.rc (100%) rename {pylint/test => tests}/functional/logging_format_interpolation_py36.txt (100%) rename {pylint/test => tests}/functional/logging_fstring_interpolation_py36.py (100%) rename {pylint/test => tests}/functional/logging_fstring_interpolation_py36.rc (100%) rename {pylint/test => tests}/functional/logging_fstring_interpolation_py36.txt (100%) rename {pylint/test => tests}/functional/logging_not_lazy.py (100%) rename {pylint/test => tests}/functional/logging_not_lazy.txt (100%) rename {pylint/test => tests}/functional/logical_tautology.py (100%) rename {pylint/test => tests}/functional/logical_tautology.txt (100%) rename {pylint/test => tests}/functional/long_lines_with_utf8.py (100%) rename {pylint/test => tests}/functional/long_lines_with_utf8.txt (100%) rename {pylint/test => tests}/functional/long_utf8_lines.py (100%) rename {pylint/test => tests}/functional/long_utf8_lines.txt (100%) rename {pylint/test => tests}/functional/lost_exception.py (100%) rename {pylint/test => tests}/functional/lost_exception.txt (100%) rename {pylint/test => tests}/functional/mapping_context.py (100%) rename {pylint/test => tests}/functional/mapping_context.txt (100%) rename {pylint/test => tests}/functional/mapping_context_py2.py (100%) rename {pylint/test => tests}/functional/mapping_context_py2.rc (100%) rename {pylint/test => tests}/functional/mapping_context_py2.txt (100%) rename {pylint/test => tests}/functional/mapping_context_py3.py (100%) rename {pylint/test => tests}/functional/mapping_context_py3.rc (100%) rename {pylint/test => tests}/functional/mapping_context_py3.txt (100%) rename {pylint/test => tests}/functional/member_checks.py (100%) rename {pylint/test => tests}/functional/member_checks.txt (100%) rename {pylint/test => tests}/functional/member_checks_hints.py (100%) rename {pylint/test => tests}/functional/member_checks_hints.rc (100%) rename {pylint/test => tests}/functional/member_checks_hints.txt (100%) rename {pylint/test => tests}/functional/member_checks_ignore_none.py (100%) rename {pylint/test => tests}/functional/member_checks_ignore_none.rc (100%) rename {pylint/test => tests}/functional/member_checks_ignore_none.txt (100%) rename {pylint/test => tests}/functional/member_checks_inference_improvements.py (100%) rename {pylint/test => tests}/functional/member_checks_no_hints.py (100%) rename {pylint/test => tests}/functional/member_checks_no_hints.rc (100%) rename {pylint/test => tests}/functional/member_checks_no_hints.txt (100%) rename {pylint/test => tests}/functional/member_checks_opaque.py (100%) rename {pylint/test => tests}/functional/member_checks_opaque.rc (100%) rename {pylint/test => tests}/functional/member_checks_opaque.txt (100%) rename {pylint/test => tests}/functional/member_checks_py37.py (100%) rename {pylint/test => tests}/functional/member_checks_py37.rc (100%) rename {pylint/test => tests}/functional/member_checks_py37.txt (100%) rename {pylint/test => tests}/functional/membership_protocol.py (100%) rename {pylint/test => tests}/functional/membership_protocol.txt (100%) rename {pylint/test => tests}/functional/membership_protocol_py2.py (100%) rename {pylint/test => tests}/functional/membership_protocol_py2.rc (100%) rename {pylint/test => tests}/functional/membership_protocol_py2.txt (100%) rename {pylint/test => tests}/functional/membership_protocol_py3.py (100%) rename {pylint/test => tests}/functional/membership_protocol_py3.rc (100%) rename {pylint/test => tests}/functional/membership_protocol_py3.txt (100%) rename {pylint/test => tests}/functional/messages_managed_by_id.py (100%) rename {pylint/test => tests}/functional/messages_managed_by_id.txt (100%) rename {pylint/test => tests}/functional/method_hidden.py (100%) rename {pylint/test => tests}/functional/method_hidden.txt (100%) rename {pylint/test => tests}/functional/misplaced_bare_raise.py (100%) rename {pylint/test => tests}/functional/misplaced_bare_raise.txt (100%) rename {pylint/test => tests}/functional/misplaced_comparison_constant.py (100%) rename {pylint/test => tests}/functional/misplaced_comparison_constant.txt (100%) rename {pylint/test => tests}/functional/misplaced_format_function.py (100%) rename {pylint/test => tests}/functional/misplaced_format_function.txt (100%) rename {pylint/test => tests}/functional/misplaced_future.py (100%) rename {pylint/test => tests}/functional/misplaced_future.txt (100%) rename {pylint/test => tests}/functional/missing_docstring.py (100%) rename {pylint/test => tests}/functional/missing_docstring.txt (100%) rename {pylint/test => tests}/functional/missing_final_newline.py (100%) rename {pylint/test => tests}/functional/missing_final_newline.txt (100%) rename {pylint/test => tests}/functional/missing_kwoa_py3.py (100%) rename {pylint/test => tests}/functional/missing_kwoa_py3.rc (100%) rename {pylint/test => tests}/functional/missing_kwoa_py3.txt (100%) rename {pylint/test => tests}/functional/missing_parentheses_for_call_in_test.py (100%) rename {pylint/test => tests}/functional/missing_parentheses_for_call_in_test.txt (100%) rename {pylint/test => tests}/functional/missing_self_argument.py (100%) rename {pylint/test => tests}/functional/missing_self_argument.txt (100%) rename {pylint/test => tests}/functional/mixed_indentation.py (100%) rename {pylint/test => tests}/functional/mixed_indentation.txt (100%) rename {pylint/test => tests}/functional/monkeypatch_method.py (100%) rename {pylint/test => tests}/functional/monkeypatch_method.txt (100%) rename {pylint/test => tests}/functional/multiple_imports.py (100%) rename {pylint/test => tests}/functional/multiple_imports.txt (100%) rename {pylint/test => tests}/functional/namePresetCamelCase.py (100%) rename {pylint/test => tests}/functional/namePresetCamelCase.rc (100%) rename {pylint/test => tests}/functional/namePresetCamelCase.txt (100%) rename {pylint/test => tests}/functional/name_preset_snake_case.py (100%) rename {pylint/test => tests}/functional/name_preset_snake_case.rc (100%) rename {pylint/test => tests}/functional/name_preset_snake_case.txt (100%) rename {pylint/test => tests}/functional/name_styles.py (100%) rename {pylint/test => tests}/functional/name_styles.rc (100%) rename {pylint/test => tests}/functional/name_styles.txt (100%) rename {pylint/test => tests}/functional/namedtuple_member_inference.py (100%) rename {pylint/test => tests}/functional/namedtuple_member_inference.txt (100%) rename {pylint/test => tests}/functional/names_in__all__.py (100%) rename {pylint/test => tests}/functional/names_in__all__.txt (100%) rename {pylint/test => tests}/functional/nested_blocks_issue1088.py (100%) rename {pylint/test => tests}/functional/nested_blocks_issue1088.txt (100%) rename {pylint/test => tests}/functional/nested_func_defined_in_loop.py (100%) rename {pylint/test => tests}/functional/no_classmethod_decorator.py (100%) rename {pylint/test => tests}/functional/no_classmethod_decorator.txt (100%) rename {pylint/test => tests}/functional/no_else_raise.py (100%) rename {pylint/test => tests}/functional/no_else_raise.txt (100%) rename {pylint/test => tests}/functional/no_else_return.py (100%) rename {pylint/test => tests}/functional/no_else_return.txt (100%) rename {pylint/test => tests}/functional/no_name_in_module.py (100%) rename {pylint/test => tests}/functional/no_name_in_module.txt (100%) rename {pylint/test => tests}/functional/no_self_argument_py37.py (100%) rename {pylint/test => tests}/functional/no_self_argument_py37.rc (100%) rename {pylint/test => tests}/functional/no_self_argument_py37.txt (100%) rename {pylint/test => tests}/functional/no_self_use.py (100%) rename {pylint/test => tests}/functional/no_self_use.txt (100%) rename {pylint/test => tests}/functional/no_self_use_py3.py (100%) rename {pylint/test => tests}/functional/no_self_use_py3.rc (100%) rename {pylint/test => tests}/functional/no_self_use_py3.txt (100%) rename {pylint/test => tests}/functional/no_staticmethod_decorator.py (100%) rename {pylint/test => tests}/functional/no_staticmethod_decorator.txt (100%) rename {pylint/test => tests}/functional/non_iterator_returned.py (100%) rename {pylint/test => tests}/functional/non_iterator_returned.txt (100%) rename {pylint/test => tests}/functional/none_dunder_protocols_py36.py (100%) rename {pylint/test => tests}/functional/none_dunder_protocols_py36.rc (100%) rename {pylint/test => tests}/functional/none_dunder_protocols_py36.txt (100%) rename {pylint/test => tests}/functional/nonexistent_operator.py (100%) rename {pylint/test => tests}/functional/nonexistent_operator.txt (100%) rename {pylint/test => tests}/functional/nonlocal_and_global.py (100%) rename {pylint/test => tests}/functional/nonlocal_and_global.rc (100%) rename {pylint/test => tests}/functional/nonlocal_and_global.txt (100%) rename {pylint/test => tests}/functional/nonlocal_without_binding.py (100%) rename {pylint/test => tests}/functional/nonlocal_without_binding.rc (100%) rename {pylint/test => tests}/functional/nonlocal_without_binding.txt (100%) rename {pylint/test => tests}/functional/not_async_context_manager.py (100%) rename {pylint/test => tests}/functional/not_async_context_manager.rc (100%) rename {pylint/test => tests}/functional/not_async_context_manager.txt (100%) rename {pylint/test => tests}/functional/not_async_context_manager_py37.py (100%) rename {pylint/test => tests}/functional/not_async_context_manager_py37.rc (100%) rename {pylint/test => tests}/functional/not_async_context_manager_py37.txt (100%) rename {pylint/test => tests}/functional/not_callable.py (100%) rename {pylint/test => tests}/functional/not_callable.txt (100%) rename {pylint/test => tests}/functional/not_context_manager.py (100%) rename {pylint/test => tests}/functional/not_context_manager.txt (100%) rename {pylint/test => tests}/functional/not_in_loop.py (100%) rename {pylint/test => tests}/functional/not_in_loop.txt (100%) rename {pylint/test => tests}/functional/old_division_manually.py (100%) rename {pylint/test => tests}/functional/old_division_manually.rc (100%) rename {pylint/test => tests}/functional/postponed_evaluation_activated.py (100%) rename {pylint/test => tests}/functional/postponed_evaluation_activated.rc (100%) rename {pylint/test => tests}/functional/postponed_evaluation_activated.txt (100%) rename {pylint/test => tests}/functional/postponed_evaluation_not_activated.py (100%) rename {pylint/test => tests}/functional/postponed_evaluation_not_activated.rc (100%) rename {pylint/test => tests}/functional/postponed_evaluation_not_activated.txt (100%) rename {pylint/test => tests}/functional/pragma_after_backslash.py (100%) rename {pylint/test => tests}/functional/preferred_module.py (100%) rename {pylint/test => tests}/functional/preferred_module.rc (100%) rename {pylint/test => tests}/functional/preferred_module.txt (100%) rename {pylint/test => tests}/functional/print_always_warns.py (100%) rename {pylint/test => tests}/functional/print_always_warns.rc (100%) rename {pylint/test => tests}/functional/print_always_warns.txt (100%) rename {pylint/test => tests}/functional/protected_access_access_different_scopes.py (100%) rename {pylint/test => tests}/functional/protected_access_access_different_scopes.rc (100%) rename {pylint/test => tests}/functional/protected_access_access_different_scopes.txt (100%) rename {pylint/test => tests}/functional/raising_format_tuple.py (100%) rename {pylint/test => tests}/functional/raising_format_tuple.txt (100%) rename {pylint/test => tests}/functional/raising_non_exception_py3.py (100%) rename {pylint/test => tests}/functional/raising_non_exception_py3.rc (100%) rename {pylint/test => tests}/functional/raising_non_exception_py3.txt (100%) rename {pylint/test => tests}/functional/raising_self.py (100%) rename {pylint/test => tests}/functional/raising_self.txt (100%) rename {pylint/test => tests}/functional/recursion_error_2667.py (100%) rename {pylint/test => tests}/functional/recursion_error_2667.txt (100%) rename {pylint/test => tests}/functional/recursion_error_2906.py (100%) rename {pylint/test => tests}/functional/recursion_error_940.py (100%) rename {pylint/test => tests}/functional/recursion_error_crash.py (100%) rename {pylint/test => tests}/functional/recursion_error_crash.txt (100%) rename {pylint/test => tests}/functional/recursion_error_crash_2683.py (100%) rename {pylint/test => tests}/functional/recursion_error_crash_2683.txt (100%) rename {pylint/test => tests}/functional/recursion_error_crash_astroid_623.py (100%) rename {pylint/test => tests}/functional/recursion_error_crash_astroid_623.txt (100%) rename {pylint/test => tests}/functional/redefine_in_handler.py (100%) rename {pylint/test => tests}/functional/redefine_in_handler.rc (100%) rename {pylint/test => tests}/functional/redefine_in_handler.txt (100%) rename {pylint/test => tests}/functional/redefined_argument_from_local.py (100%) rename {pylint/test => tests}/functional/redefined_argument_from_local.txt (100%) rename {pylint/test => tests}/functional/redefined_builtin.py (100%) rename {pylint/test => tests}/functional/redefined_builtin.txt (100%) rename {pylint/test => tests}/functional/redundant_unittest_assert.py (100%) rename {pylint/test => tests}/functional/redundant_unittest_assert.txt (100%) rename {pylint/test => tests}/functional/regression_1326_crash_uninferable.py (100%) rename {pylint/test => tests}/functional/regression_2443_duplicate_bases.py (100%) rename {pylint/test => tests}/functional/regression_2443_duplicate_bases.rc (100%) rename {pylint/test => tests}/functional/regression_2937_ifexp.py (100%) rename {pylint/test => tests}/functional/regression_no_value_for_parameter.py (100%) rename {pylint/test => tests}/functional/regression_no_value_for_parameter.txt (100%) rename {pylint/test => tests}/functional/reimported.py (100%) rename {pylint/test => tests}/functional/reimported.txt (100%) rename {pylint/test => tests}/functional/repeated_keyword.py (100%) rename {pylint/test => tests}/functional/repeated_keyword.txt (100%) rename {pylint/test => tests}/functional/return_in_init.py (100%) rename {pylint/test => tests}/functional/return_in_init.txt (100%) rename {pylint/test => tests}/functional/return_outside_function.py (100%) rename {pylint/test => tests}/functional/return_outside_function.txt (100%) rename {pylint/test => tests}/functional/reused_outer_loop_variable.py (100%) rename {pylint/test => tests}/functional/reused_outer_loop_variable.txt (100%) rename {pylint/test => tests}/functional/reused_outer_loop_variable_py3.py (100%) rename {pylint/test => tests}/functional/reused_outer_loop_variable_py3.rc (100%) rename {pylint/test => tests}/functional/reused_outer_loop_variable_py3.txt (100%) rename {pylint/test => tests}/functional/self_cls_assignment.py (100%) rename {pylint/test => tests}/functional/self_cls_assignment.txt (100%) rename {pylint/test => tests}/functional/signature_differs.py (100%) rename {pylint/test => tests}/functional/signature_differs.txt (100%) rename {pylint/test => tests}/functional/simplifiable_if_expression.py (100%) rename {pylint/test => tests}/functional/simplifiable_if_expression.txt (100%) rename {pylint/test => tests}/functional/simplifiable_if_statement.py (100%) rename {pylint/test => tests}/functional/simplifiable_if_statement.txt (100%) rename {pylint/test => tests}/functional/simplify_chained_comparison.py (100%) rename {pylint/test => tests}/functional/simplify_chained_comparison.txt (100%) rename {pylint/test => tests}/functional/singledispatch_functions.py (100%) rename {pylint/test => tests}/functional/singledispatch_functions.rc (100%) rename {pylint/test => tests}/functional/singledispatch_functions.txt (100%) rename {pylint/test => tests}/functional/singledispatch_functions_py3.py (100%) rename {pylint/test => tests}/functional/singledispatch_functions_py3.rc (100%) rename {pylint/test => tests}/functional/singledispatch_functions_py3.txt (100%) rename {pylint/test => tests}/functional/singleton_comparison.py (100%) rename {pylint/test => tests}/functional/singleton_comparison.txt (100%) rename {pylint/test => tests}/functional/slots_checks.py (100%) rename {pylint/test => tests}/functional/slots_checks.txt (100%) rename {pylint/test => tests}/functional/socketerror_import.py (100%) rename {pylint/test => tests}/functional/star_needs_assignment_target.py (100%) rename {pylint/test => tests}/functional/star_needs_assignment_target.rc (100%) rename {pylint/test => tests}/functional/star_needs_assignment_target.txt (100%) rename {pylint/test => tests}/functional/star_needs_assignment_target_py35.py (100%) rename {pylint/test => tests}/functional/star_needs_assignment_target_py35.rc (100%) rename {pylint/test => tests}/functional/star_needs_assignment_target_py35.txt (100%) rename {pylint/test => tests}/functional/statement_without_effect.py (100%) rename {pylint/test => tests}/functional/statement_without_effect.txt (100%) rename {pylint/test => tests}/functional/statement_without_effect_py36.py (100%) rename {pylint/test => tests}/functional/statement_without_effect_py36.rc (100%) rename {pylint/test => tests}/functional/statement_without_effect_py36.txt (100%) rename {pylint/test => tests}/functional/stop_iteration_inside_generator.py (100%) rename {pylint/test => tests}/functional/stop_iteration_inside_generator.rc (100%) rename {pylint/test => tests}/functional/stop_iteration_inside_generator.txt (100%) rename {pylint/test => tests}/functional/string_formatting.py (100%) rename {pylint/test => tests}/functional/string_formatting.txt (100%) rename {pylint/test => tests}/functional/string_formatting_disable.py (100%) rename {pylint/test => tests}/functional/string_formatting_disable.rc (100%) rename {pylint/test => tests}/functional/string_formatting_disable.txt (100%) rename {pylint/test => tests}/functional/string_formatting_failed_inference.py (100%) rename {pylint/test => tests}/functional/string_formatting_failed_inference_py35.py (100%) rename {pylint/test => tests}/functional/string_formatting_failed_inference_py35.rc (100%) rename {pylint/test => tests}/functional/string_formatting_py27.py (100%) rename {pylint/test => tests}/functional/string_formatting_py27.rc (100%) rename {pylint/test => tests}/functional/string_formatting_py27.txt (100%) rename {pylint/test => tests}/functional/string_formatting_py3.py (100%) rename {pylint/test => tests}/functional/string_formatting_py3.rc (100%) rename {pylint/test => tests}/functional/string_formatting_py3.txt (100%) rename {pylint/test => tests}/functional/subprocess_popen_preexec_fn.py (100%) rename {pylint/test => tests}/functional/subprocess_popen_preexec_fn.txt (100%) rename {pylint/test => tests}/functional/subprocess_run_check35.py (100%) rename {pylint/test => tests}/functional/subprocess_run_check35.rc (100%) rename {pylint/test => tests}/functional/subprocess_run_check35.txt (100%) rename {pylint/test => tests}/functional/super_checks.py (100%) rename {pylint/test => tests}/functional/super_checks.txt (100%) rename {pylint/test => tests}/functional/superfluous_parens.py (100%) rename {pylint/test => tests}/functional/superfluous_parens.txt (100%) rename {pylint/test => tests}/functional/suspicious_str_strip_call.py (100%) rename {pylint/test => tests}/functional/suspicious_str_strip_call.rc (100%) rename {pylint/test => tests}/functional/suspicious_str_strip_call.txt (100%) rename {pylint/test => tests}/functional/suspicious_str_strip_call_py3.py (100%) rename {pylint/test => tests}/functional/suspicious_str_strip_call_py3.rc (100%) rename {pylint/test => tests}/functional/suspicious_str_strip_call_py3.txt (100%) rename {pylint/test => tests}/functional/syntax_error.py (100%) rename {pylint/test => tests}/functional/syntax_error.rc (100%) rename {pylint/test => tests}/functional/syntax_error.txt (100%) rename {pylint/test => tests}/functional/syntax_error_jython.py (100%) rename {pylint/test => tests}/functional/syntax_error_jython.rc (100%) rename {pylint/test => tests}/functional/syntax_error_jython.txt (100%) rename {pylint/test => tests}/functional/sys_stream_regression_1004.py (100%) rename {pylint/test => tests}/functional/sys_stream_regression_1004.rc (100%) rename {pylint/test => tests}/functional/sys_stream_regression_1004.txt (100%) rename {pylint/test => tests}/functional/ternary.py (100%) rename {pylint/test => tests}/functional/ternary.txt (100%) rename {pylint/test => tests}/functional/test_compile.py (100%) rename {pylint/test => tests}/functional/tokenize_error.py (100%) rename {pylint/test => tests}/functional/tokenize_error.rc (100%) rename {pylint/test => tests}/functional/tokenize_error.txt (100%) rename {pylint/test => tests}/functional/tokenize_error_jython.py (100%) rename {pylint/test => tests}/functional/tokenize_error_jython.rc (100%) rename {pylint/test => tests}/functional/tokenize_error_jython.txt (100%) rename {pylint/test => tests}/functional/too_few_public_methods.py (100%) rename {pylint/test => tests}/functional/too_few_public_methods.txt (100%) rename {pylint/test => tests}/functional/too_few_public_methods_37.py (100%) rename {pylint/test => tests}/functional/too_few_public_methods_37.rc (100%) rename {pylint/test => tests}/functional/too_few_public_methods_37.txt (100%) rename {pylint/test => tests}/functional/too_many_ancestors.py (100%) rename {pylint/test => tests}/functional/too_many_ancestors.txt (100%) rename {pylint/test => tests}/functional/too_many_arguments.py (100%) rename {pylint/test => tests}/functional/too_many_arguments.txt (100%) rename {pylint/test => tests}/functional/too_many_arguments_issue_1045.py (100%) rename {pylint/test => tests}/functional/too_many_arguments_issue_1045.rc (100%) rename {pylint/test => tests}/functional/too_many_arguments_issue_1045.txt (100%) rename {pylint/test => tests}/functional/too_many_boolean_expressions.py (100%) rename {pylint/test => tests}/functional/too_many_boolean_expressions.txt (100%) rename {pylint/test => tests}/functional/too_many_branches.py (100%) rename {pylint/test => tests}/functional/too_many_branches.txt (100%) rename {pylint/test => tests}/functional/too_many_instance_attributes.py (100%) rename {pylint/test => tests}/functional/too_many_instance_attributes.txt (100%) rename {pylint/test => tests}/functional/too_many_lines.py (100%) rename {pylint/test => tests}/functional/too_many_lines.txt (100%) rename {pylint/test => tests}/functional/too_many_lines_disabled.py (100%) rename {pylint/test => tests}/functional/too_many_locals.py (100%) rename {pylint/test => tests}/functional/too_many_locals.txt (100%) rename {pylint/test => tests}/functional/too_many_nested_blocks.py (100%) rename {pylint/test => tests}/functional/too_many_nested_blocks.txt (100%) rename {pylint/test => tests}/functional/too_many_public_methods.py (100%) rename {pylint/test => tests}/functional/too_many_public_methods.txt (100%) rename {pylint/test => tests}/functional/too_many_return_statements.py (100%) rename {pylint/test => tests}/functional/too_many_return_statements.txt (100%) rename {pylint/test => tests}/functional/too_many_star_expressions.py (100%) rename {pylint/test => tests}/functional/too_many_star_expressions.rc (100%) rename {pylint/test => tests}/functional/too_many_star_expressions.txt (100%) rename {pylint/test => tests}/functional/too_many_statements.py (100%) rename {pylint/test => tests}/functional/too_many_statements.txt (100%) rename {pylint/test => tests}/functional/trailing_comma_tuple.py (100%) rename {pylint/test => tests}/functional/trailing_comma_tuple.rc (100%) rename {pylint/test => tests}/functional/trailing_comma_tuple.txt (100%) rename {pylint/test => tests}/functional/trailing_newlines.py (100%) rename {pylint/test => tests}/functional/trailing_newlines.txt (100%) rename {pylint/test => tests}/functional/trailing_whitespaces.py (100%) rename {pylint/test => tests}/functional/trailing_whitespaces.txt (100%) rename {pylint/test => tests}/functional/try_except_raise.py (100%) rename {pylint/test => tests}/functional/try_except_raise.txt (100%) rename {pylint/test => tests}/functional/try_except_raise_crash.py (100%) rename {pylint/test => tests}/functional/try_except_raise_crash.txt (100%) rename {pylint/test => tests}/functional/typing_use.py (100%) rename {pylint/test => tests}/functional/typing_use.rc (100%) rename {pylint/test => tests}/functional/typing_use.txt (100%) rename {pylint/test => tests}/functional/unbalanced_tuple_unpacking.py (100%) rename {pylint/test => tests}/functional/unbalanced_tuple_unpacking.txt (100%) rename {pylint/test => tests}/functional/unbalanced_tuple_unpacking_py30.py (100%) rename {pylint/test => tests}/functional/unbalanced_tuple_unpacking_py30.rc (100%) rename {pylint/test => tests}/functional/undefined_loop_variable.py (100%) rename {pylint/test => tests}/functional/undefined_loop_variable.txt (100%) rename {pylint/test => tests}/functional/undefined_variable.py (100%) rename {pylint/test => tests}/functional/undefined_variable.txt (100%) rename {pylint/test => tests}/functional/undefined_variable_py30.py (100%) rename {pylint/test => tests}/functional/undefined_variable_py30.rc (100%) rename {pylint/test => tests}/functional/undefined_variable_py30.txt (100%) rename {pylint/test => tests}/functional/unexpected_special_method_signature.py (100%) rename {pylint/test => tests}/functional/unexpected_special_method_signature.txt (100%) rename {pylint/test => tests}/functional/ungrouped_imports.py (100%) rename {pylint/test => tests}/functional/ungrouped_imports.txt (100%) rename {pylint/test => tests}/functional/ungrouped_imports_isort_compatible.py (100%) rename {pylint/test => tests}/functional/ungrouped_imports_isort_compatible.txt (100%) rename {pylint/test => tests}/functional/unhashable_dict_key.py (100%) rename {pylint/test => tests}/functional/unhashable_dict_key.txt (100%) rename {pylint/test => tests}/functional/unidiomatic_typecheck.py (100%) rename {pylint/test => tests}/functional/unidiomatic_typecheck.txt (100%) rename {pylint/test => tests}/functional/uninferable_all_object.py (100%) rename {pylint/test => tests}/functional/unknown_encoding_jython.py (100%) rename {pylint/test => tests}/functional/unknown_encoding_jython.rc (100%) rename {pylint/test => tests}/functional/unknown_encoding_jython.txt (100%) rename {pylint/test => tests}/functional/unknown_encoding_py29.py (100%) rename {pylint/test => tests}/functional/unknown_encoding_py29.rc (100%) rename {pylint/test => tests}/functional/unknown_encoding_py29.txt (100%) rename {pylint/test => tests}/functional/unknown_encoding_pypy.py (100%) rename {pylint/test => tests}/functional/unknown_encoding_pypy.rc (100%) rename {pylint/test => tests}/functional/unknown_encoding_pypy.txt (100%) rename {pylint/test => tests}/functional/unnecessary_lambda.py (100%) rename {pylint/test => tests}/functional/unnecessary_lambda.txt (100%) rename {pylint/test => tests}/functional/unnecessary_pass.py (100%) rename {pylint/test => tests}/functional/unnecessary_pass.txt (100%) rename {pylint/test => tests}/functional/unneeded_not.py (100%) rename {pylint/test => tests}/functional/unneeded_not.txt (100%) rename {pylint/test => tests}/functional/unpacked_exceptions.py (100%) rename {pylint/test => tests}/functional/unpacked_exceptions.rc (100%) rename {pylint/test => tests}/functional/unpacked_exceptions.txt (100%) rename {pylint/test => tests}/functional/unpacking.py (100%) rename {pylint/test => tests}/functional/unpacking_generalizations.py (100%) rename {pylint/test => tests}/functional/unpacking_generalizations.rc (100%) rename {pylint/test => tests}/functional/unpacking_generalizations.txt (100%) rename {pylint/test => tests}/functional/unpacking_non_sequence.py (100%) rename {pylint/test => tests}/functional/unpacking_non_sequence.txt (100%) rename {pylint/test => tests}/functional/unreachable.py (100%) rename {pylint/test => tests}/functional/unreachable.txt (100%) rename {pylint/test => tests}/functional/unrecognized_inline_option.py (100%) rename {pylint/test => tests}/functional/unrecognized_inline_option.txt (100%) rename {pylint/test => tests}/functional/unsubscriptable_value.py (100%) rename {pylint/test => tests}/functional/unsubscriptable_value.txt (100%) rename {pylint/test => tests}/functional/unsubscriptable_value_py37.py (100%) rename {pylint/test => tests}/functional/unsubscriptable_value_py37.rc (100%) rename {pylint/test => tests}/functional/unsubscriptable_value_py37.txt (100%) rename {pylint/test => tests}/functional/unsupported_assignment_operation.py (100%) rename {pylint/test => tests}/functional/unsupported_assignment_operation.txt (100%) rename {pylint/test => tests}/functional/unsupported_binary_operation.py (100%) rename {pylint/test => tests}/functional/unsupported_binary_operation.rc (100%) rename {pylint/test => tests}/functional/unsupported_binary_operation.txt (100%) rename {pylint/test => tests}/functional/unsupported_delete_operation.py (100%) rename {pylint/test => tests}/functional/unsupported_delete_operation.txt (100%) rename {pylint/test => tests}/functional/unused_argument.py (100%) rename {pylint/test => tests}/functional/unused_argument.txt (100%) rename {pylint/test => tests}/functional/unused_argument_py3.py (100%) rename {pylint/test => tests}/functional/unused_argument_py3.rc (100%) rename {pylint/test => tests}/functional/unused_argument_py3.txt (100%) rename {pylint/test => tests}/functional/unused_global_variable1.py (100%) rename {pylint/test => tests}/functional/unused_global_variable2.py (100%) rename {pylint/test => tests}/functional/unused_global_variable2.rc (100%) rename {pylint/test => tests}/functional/unused_global_variable2.txt (100%) rename {pylint/test => tests}/functional/unused_global_variable3.py (100%) rename {pylint/test => tests}/functional/unused_global_variable4.py (100%) rename {pylint/test => tests}/functional/unused_global_variable4.rc (100%) rename {pylint/test => tests}/functional/unused_global_variable4.txt (100%) rename {pylint/test => tests}/functional/unused_import.py (100%) rename {pylint/test => tests}/functional/unused_import.txt (100%) rename {pylint/test => tests}/functional/unused_import_assigned_to.py (100%) rename {pylint/test => tests}/functional/unused_typing_imports.py (100%) rename {pylint/test => tests}/functional/unused_typing_imports.rc (100%) rename {pylint/test => tests}/functional/unused_variable.py (100%) rename {pylint/test => tests}/functional/unused_variable.txt (100%) rename {pylint/test => tests}/functional/unused_variable_py36.py (100%) rename {pylint/test => tests}/functional/unused_variable_py36.rc (100%) rename {pylint/test => tests}/functional/unused_variable_py36.txt (100%) rename {pylint/test => tests}/functional/used_before_assignment_488.py (100%) rename {pylint/test => tests}/functional/used_before_assignment_issue1081.py (100%) rename {pylint/test => tests}/functional/used_before_assignment_issue1081.txt (100%) rename {pylint/test => tests}/functional/used_before_assignment_issue853.py (100%) rename {pylint/test => tests}/functional/used_before_assignment_nonlocal.py (100%) rename {pylint/test => tests}/functional/used_before_assignment_nonlocal.rc (100%) rename {pylint/test => tests}/functional/used_before_assignment_nonlocal.txt (100%) rename {pylint/test => tests}/functional/used_prior_global_declaration.py (100%) rename {pylint/test => tests}/functional/used_prior_global_declaration.rc (100%) rename {pylint/test => tests}/functional/used_prior_global_declaration.txt (100%) rename {pylint/test => tests}/functional/useless-import-alias.py (100%) rename {pylint/test => tests}/functional/useless-import-alias.txt (100%) rename {pylint/test => tests}/functional/useless_else_on_loop.py (100%) rename {pylint/test => tests}/functional/useless_else_on_loop.txt (100%) rename {pylint/test => tests}/functional/useless_object_inheritance.py (100%) rename {pylint/test => tests}/functional/useless_object_inheritance.txt (100%) rename {pylint/test => tests}/functional/useless_return.py (100%) rename {pylint/test => tests}/functional/useless_return.txt (100%) rename {pylint/test => tests}/functional/useless_super_delegation.py (100%) rename {pylint/test => tests}/functional/useless_super_delegation.txt (100%) rename {pylint/test => tests}/functional/useless_super_delegation_py3.py (100%) rename {pylint/test => tests}/functional/useless_super_delegation_py3.rc (100%) rename {pylint/test => tests}/functional/useless_super_delegation_py3.txt (100%) rename {pylint/test => tests}/functional/useless_super_delegation_py35.py (100%) rename {pylint/test => tests}/functional/useless_super_delegation_py35.rc (100%) rename {pylint/test => tests}/functional/useless_super_delegation_py35.txt (100%) rename {pylint/test => tests}/functional/using_constant_test.py (100%) rename {pylint/test => tests}/functional/using_constant_test.txt (100%) rename {pylint/test => tests}/functional/wildcard_import.py (100%) rename {pylint/test => tests}/functional/wildcard_import.txt (100%) rename {pylint/test => tests}/functional/wildcard_import_allowed.py (100%) rename {pylint/test => tests}/functional/wildcard_import_allowed.rc (100%) rename {pylint/test => tests}/functional/wildcard_import_allowed.txt (100%) rename {pylint/test => tests}/functional/with_used_before_assign.py (100%) rename {pylint/test => tests}/functional/with_used_before_assign.txt (100%) rename {pylint/test => tests}/functional/with_using_generator.py (100%) rename {pylint/test => tests}/functional/with_using_generator.txt (100%) rename {pylint/test => tests}/functional/wrong_exception_operation.py (100%) rename {pylint/test => tests}/functional/wrong_exception_operation.txt (100%) rename {pylint/test => tests}/functional/wrong_import_order.py (100%) rename {pylint/test => tests}/functional/wrong_import_order.txt (100%) rename {pylint/test => tests}/functional/wrong_import_order2.py (100%) rename {pylint/test => tests}/functional/wrong_import_position.py (100%) rename {pylint/test => tests}/functional/wrong_import_position.txt (100%) rename {pylint/test => tests}/functional/wrong_import_position10.py (100%) rename {pylint/test => tests}/functional/wrong_import_position11.py (100%) rename {pylint/test => tests}/functional/wrong_import_position11.txt (100%) rename {pylint/test => tests}/functional/wrong_import_position12.py (100%) rename {pylint/test => tests}/functional/wrong_import_position12.txt (100%) rename {pylint/test => tests}/functional/wrong_import_position13.py (100%) rename {pylint/test => tests}/functional/wrong_import_position13.txt (100%) rename {pylint/test => tests}/functional/wrong_import_position14.py (100%) rename {pylint/test => tests}/functional/wrong_import_position14.txt (100%) rename {pylint/test => tests}/functional/wrong_import_position15.py (100%) rename {pylint/test => tests}/functional/wrong_import_position2.py (100%) rename {pylint/test => tests}/functional/wrong_import_position3.py (100%) rename {pylint/test => tests}/functional/wrong_import_position4.py (100%) rename {pylint/test => tests}/functional/wrong_import_position5.py (100%) rename {pylint/test => tests}/functional/wrong_import_position6.py (100%) rename {pylint/test => tests}/functional/wrong_import_position7.py (100%) rename {pylint/test => tests}/functional/wrong_import_position8.py (100%) rename {pylint/test => tests}/functional/wrong_import_position9.py (100%) rename {pylint/test => tests}/functional/wrong_import_position_exclude_dunder_main.py (100%) rename {pylint/test => tests}/functional/wrong_import_position_exclude_dunder_main.txt (100%) rename {pylint/test => tests}/functional/yield_from_iterable_py33.py (100%) rename {pylint/test => tests}/functional/yield_from_iterable_py33.rc (100%) rename {pylint/test => tests}/functional/yield_from_iterable_py33.txt (100%) rename {pylint/test => tests}/functional/yield_from_outside_func.py (100%) rename {pylint/test => tests}/functional/yield_from_outside_func.rc (100%) rename {pylint/test => tests}/functional/yield_from_outside_func.txt (100%) rename {pylint/test => tests}/functional/yield_inside_async_function.py (100%) rename {pylint/test => tests}/functional/yield_inside_async_function.rc (100%) rename {pylint/test => tests}/functional/yield_inside_async_function.txt (100%) rename {pylint/test => tests}/functional/yield_inside_async_function_py36.py (100%) rename {pylint/test => tests}/functional/yield_inside_async_function_py36.rc (100%) rename {pylint/test => tests}/functional/yield_inside_async_function_py36.txt (100%) rename {pylint/test => tests}/functional/yield_outside_func.py (100%) rename {pylint/test => tests}/functional/yield_outside_func.txt (100%) rename {pylint/test => tests}/input/__init__.py (100%) rename {pylint/test => tests}/input/func_3k_removed_stuff_py_30.py (100%) rename {pylint/test => tests}/input/func_bad_cont_dictcomp_py27.py (100%) rename {pylint/test => tests}/input/func_bug113231.py (100%) rename {pylint/test => tests}/input/func_disable_linebased.py (100%) rename {pylint/test => tests}/input/func_dotted_ancestor.py (100%) rename {pylint/test => tests}/input/func_e0012.py (100%) rename {pylint/test => tests}/input/func_e0204.py (100%) rename {pylint/test => tests}/input/func_e12xx.py (100%) rename {pylint/test => tests}/input/func_e13xx.py (100%) rename {pylint/test => tests}/input/func_excess_escapes.py (100%) rename {pylint/test => tests}/input/func_first_arg.py (100%) rename {pylint/test => tests}/input/func_i0011.py (100%) rename {pylint/test => tests}/input/func_i0012.py (100%) rename {pylint/test => tests}/input/func_i0013.py (100%) rename {pylint/test => tests}/input/func_i0014.py (100%) rename {pylint/test => tests}/input/func_i0020.py (100%) rename {pylint/test => tests}/input/func_i0022.py (100%) rename {pylint/test => tests}/input/func_logging_not_lazy_with_logger.py (100%) rename {pylint/test => tests}/input/func_loopvar_in_dict_comp_py27.py (100%) rename {pylint/test => tests}/input/func_module___dict__.py (100%) rename {pylint/test => tests}/input/func_nameerror_on_string_substitution.py (100%) rename {pylint/test => tests}/input/func_no_dummy_redefined.py (100%) rename {pylint/test => tests}/input/func_noerror___init___return_from_inner_function.py (100%) rename {pylint/test => tests}/input/func_noerror_access_attr_before_def_false_positive.py (100%) rename {pylint/test => tests}/input/func_noerror_base_init_vars.py (100%) rename {pylint/test => tests}/input/func_noerror_builtin_module_test.py (100%) rename {pylint/test => tests}/input/func_noerror_class_attributes.py (100%) rename {pylint/test => tests}/input/func_noerror_classes_meth_could_be_a_function.py (100%) rename {pylint/test => tests}/input/func_noerror_classes_protected_member_access.py (100%) rename {pylint/test => tests}/input/func_noerror_decorator_scope.py (100%) rename {pylint/test => tests}/input/func_noerror_e1101_9588_base_attr_aug_assign.py (100%) rename {pylint/test => tests}/input/func_noerror_external_classmethod_crash.py (100%) rename {pylint/test => tests}/input/func_noerror_inner_classes.py (100%) rename {pylint/test => tests}/input/func_noerror_lambda_use_before_assign.py (100%) rename {pylint/test => tests}/input/func_noerror_mcs_attr_access.py (100%) rename {pylint/test => tests}/input/func_noerror_new_style_class_py_30.py (100%) rename {pylint/test => tests}/input/func_noerror_no_warning_docstring.py (100%) rename {pylint/test => tests}/input/func_noerror_object_as_class_attribute.py (100%) rename {pylint/test => tests}/input/func_noerror_overloaded_operator.py (100%) rename {pylint/test => tests}/input/func_noerror_property_affectation_py26.py (100%) rename {pylint/test => tests}/input/func_noerror_yield_assign_py25.py (100%) rename {pylint/test => tests}/input/func_noerror_yield_return_mix.py (100%) rename {pylint/test => tests}/input/func_nonregr___file___global.py (100%) rename {pylint/test => tests}/input/func_return_yield_mix_py_33.py (100%) rename {pylint/test => tests}/input/func_typecheck_callfunc_assigment.py (100%) rename {pylint/test => tests}/input/func_unused_import_py30.py (100%) rename {pylint/test => tests}/input/func_variables_unused_name_from_wilcard_import.py (100%) rename {pylint/test => tests}/input/func_w0122_py_30.py (100%) rename {pylint/test => tests}/input/func_w0233.py (100%) rename {pylint/test => tests}/input/func_w0332_py_30.py (100%) rename {pylint/test => tests}/input/func_w0401.py (100%) rename {pylint/test => tests}/input/func_w0401_disabled.py (100%) rename {pylint/test => tests}/input/func_w0401_disabled_in_func.py (100%) rename {pylint/test => tests}/input/func_w0401_package/__init__.py (100%) rename {pylint/test => tests}/input/func_w0401_package/all_the_things.py (100%) rename {pylint/test => tests}/input/func_w0401_package/thing1.py (100%) rename {pylint/test => tests}/input/func_w0401_package/thing2.py (100%) rename {pylint/test => tests}/input/func_w0404.py (100%) rename {pylint/test => tests}/input/func_w0405.py (100%) rename {pylint/test => tests}/input/func_w0406.py (100%) rename {pylint/test => tests}/input/func_w0611.py (100%) rename {pylint/test => tests}/input/func_w0612.py (100%) rename {pylint/test => tests}/input/func_w0613.py (100%) rename {pylint/test => tests}/input/func_w0623_py30.py (100%) rename {pylint/test => tests}/input/func_w0801.py (100%) rename {pylint/test => tests}/input/hide_code_with_imports.py (100%) rename {pylint/test => tests}/input/ignore_except_pass_by_default.py (100%) rename {pylint/test => tests}/input/multiline-import (100%) rename {pylint/test => tests}/input/noext (100%) rename {pylint/test => tests}/input/not__init__.py (100%) rename {pylint/test => tests}/input/similar1 (100%) rename {pylint/test => tests}/input/similar2 (100%) rename {pylint/test => tests}/input/w0401_cycle.py (100%) rename {pylint/test => tests}/input/w0801_same.py (100%) rename {pylint/test => tests}/message/unittest_message.py (100%) rename {pylint/test => tests}/message/unittest_message_store.py (100%) rename {pylint/test => tests}/messages/builtin_module.txt (100%) rename {pylint/test => tests}/messages/func_3k_removed_stuff_py_30.txt (100%) rename {pylint/test => tests}/messages/func_bad_cont_dictcomp_py27.txt (100%) rename {pylint/test => tests}/messages/func_bug113231.txt (100%) rename {pylint/test => tests}/messages/func_disable_linebased.txt (100%) rename {pylint/test => tests}/messages/func_disable_linebased_py30.txt (100%) rename {pylint/test => tests}/messages/func_dotted_ancestor.txt (100%) rename {pylint/test => tests}/messages/func_e0012.txt (100%) rename {pylint/test => tests}/messages/func_e0204.txt (100%) rename {pylint/test => tests}/messages/func_e12xx.txt (100%) rename {pylint/test => tests}/messages/func_e13xx.txt (100%) rename {pylint/test => tests}/messages/func_e13xx_py30.txt (100%) rename {pylint/test => tests}/messages/func_excess_escapes.txt (100%) rename {pylint/test => tests}/messages/func_first_arg.txt (100%) rename {pylint/test => tests}/messages/func_i0011.txt (100%) rename {pylint/test => tests}/messages/func_i0012.txt (100%) rename {pylint/test => tests}/messages/func_i0013.txt (100%) rename {pylint/test => tests}/messages/func_i0014.txt (100%) rename {pylint/test => tests}/messages/func_i0020.txt (100%) rename {pylint/test => tests}/messages/func_i0022.txt (100%) rename {pylint/test => tests}/messages/func_logging_not_lazy_with_logger.txt (100%) rename {pylint/test => tests}/messages/func_loopvar_in_dict_comp_py27.txt (100%) rename {pylint/test => tests}/messages/func_module___dict__.txt (100%) rename {pylint/test => tests}/messages/func_nameerror_on_string_substitution.txt (100%) rename {pylint/test => tests}/messages/func_no_dummy_redefined.txt (100%) rename {pylint/test => tests}/messages/func_nonregr___file___global.txt (100%) rename {pylint/test => tests}/messages/func_raw_escapes.txt (100%) rename {pylint/test => tests}/messages/func_return_yield_mix_py_33.txt (100%) rename {pylint/test => tests}/messages/func_toolonglines_py30.txt (100%) rename {pylint/test => tests}/messages/func_typecheck_callfunc_assigment.txt (100%) rename {pylint/test => tests}/messages/func_typecheck_getattr_py30.txt (100%) rename {pylint/test => tests}/messages/func_typecheck_non_callable_call.txt (100%) rename {pylint/test => tests}/messages/func_unicode_literal_py26.txt (100%) rename {pylint/test => tests}/messages/func_unicode_literal_py274.txt (100%) rename {pylint/test => tests}/messages/func_unused_import_py30.txt (100%) rename {pylint/test => tests}/messages/func_use_for_or_listcomp_var_py29.txt (100%) rename {pylint/test => tests}/messages/func_use_for_or_listcomp_var_py30.txt (100%) rename {pylint/test => tests}/messages/func_variables_unused_name_from_wilcard_import.txt (100%) rename {pylint/test => tests}/messages/func_w0122_py_30.txt (100%) rename {pylint/test => tests}/messages/func_w0233.txt (100%) rename {pylint/test => tests}/messages/func_w0312.txt (100%) rename {pylint/test => tests}/messages/func_w0332_py_30.txt (100%) rename {pylint/test => tests}/messages/func_w0401.txt (100%) rename {pylint/test => tests}/messages/func_w0401_disabled.txt (100%) rename {pylint/test => tests}/messages/func_w0401_disabled_in_func.txt (100%) rename {pylint/test => tests}/messages/func_w0401_package.txt (100%) rename {pylint/test => tests}/messages/func_w0404.txt (100%) rename {pylint/test => tests}/messages/func_w0405.txt (100%) rename {pylint/test => tests}/messages/func_w0406.txt (100%) rename {pylint/test => tests}/messages/func_w0611.txt (100%) rename {pylint/test => tests}/messages/func_w0612.txt (100%) rename {pylint/test => tests}/messages/func_w0613.txt (100%) rename {pylint/test => tests}/messages/func_w0622.txt (100%) rename {pylint/test => tests}/messages/func_w0623.txt (100%) rename {pylint/test => tests}/messages/func_w0623_py30.txt (100%) rename {pylint/test => tests}/messages/func_w0623_py_30.txt (100%) rename {pylint/test => tests}/messages/func_w0801.txt (100%) rename {pylint/test => tests}/messages/func_with_without_as_py25.txt (100%) rename {pylint/test => tests}/regrtest_data/.pylintrc (100%) rename {pylint/test => tests}/regrtest_data/absimp/__init__.py (100%) rename {pylint/test => tests}/regrtest_data/absimp/string.py (100%) rename {pylint/test => tests}/regrtest_data/application_crash.py (100%) rename {pylint/test => tests}/regrtest_data/bad_package/__init__.py (100%) rename {pylint/test => tests}/regrtest_data/bad_package/wrong.py (100%) rename {pylint/test => tests}/regrtest_data/beyond_top/__init__.py (100%) rename {pylint/test => tests}/regrtest_data/beyond_top/data.py (100%) rename {pylint/test => tests}/regrtest_data/classdoc_usage.py (100%) rename {pylint/test => tests}/regrtest_data/comments_pylintrc (100%) rename {pylint/test => tests}/regrtest_data/decimal_inference.py (100%) rename {pylint/test => tests}/regrtest_data/descriptor_crash.py (100%) rename {pylint/test => tests}/regrtest_data/dummy/__init__.py (100%) rename {pylint/test => tests}/regrtest_data/dummy/another.py (100%) rename {pylint/test => tests}/regrtest_data/dummy/dummy.py (100%) rename {pylint/test => tests}/regrtest_data/dummy_plugin.rc (100%) rename {pylint/test => tests}/regrtest_data/dummy_plugin/dummy_conf_plugin.py (100%) rename {pylint/test => tests}/regrtest_data/dummy_plugin/dummy_plugin.py (100%) rename {pylint/test => tests}/regrtest_data/empty.py (100%) rename {pylint/test => tests}/regrtest_data/func_block_disable_msg.py (100%) rename {pylint/test => tests}/regrtest_data/import_assign.py (100%) rename {pylint/test => tests}/regrtest_data/import_package_subpackage_module.py (100%) rename {pylint/test => tests}/regrtest_data/import_something.py (100%) rename {pylint/test => tests}/regrtest_data/init_wildcard/__init__.py (100%) rename {pylint/test => tests}/regrtest_data/meta.py (100%) rename {pylint/test => tests}/regrtest_data/module_global.py (100%) rename {pylint/test => tests}/regrtest_data/no_stdout_encoding.py (100%) rename {pylint/test => tests}/regrtest_data/numarray_import.py (100%) rename {pylint/test => tests}/regrtest_data/numarray_inf.py (100%) rename {pylint/test => tests}/regrtest_data/package/AudioTime.py (100%) rename {pylint/test => tests}/regrtest_data/package/__init__.py (100%) rename {pylint/test => tests}/regrtest_data/package/subpackage/__init__.py (100%) rename {pylint/test => tests}/regrtest_data/package/subpackage/module.py (100%) rename {pylint/test => tests}/regrtest_data/package_all/__init__.py (100%) rename {pylint/test => tests}/regrtest_data/package_all/notmissing.py (100%) rename {pylint/test => tests}/regrtest_data/precedence_test.py (100%) rename {pylint/test => tests}/regrtest_data/py3k-disabled.rc (100%) rename {pylint/test => tests}/regrtest_data/py3k_error_flag.py (100%) rename {pylint/test => tests}/regrtest_data/py3k_errors_and_warnings.py (100%) rename {pylint/test => tests}/regrtest_data/special_attr_scope_lookup_crash.py (100%) rename {pylint/test => tests}/regrtest_data/syntax_error.py (100%) rename {pylint/test => tests}/regrtest_data/test_pylintrc_comments.py (100%) rename {pylint/test => tests}/regrtest_data/try_finally_disable_msg_crash.py (100%) rename {pylint/test => tests}/regrtest_data/unused_variable.py (100%) rename {pylint/test => tests}/regrtest_data/wildcard.py (100%) rename {pylint/test => tests}/regrtest_data/wrong_import_position.py (100%) rename {pylint/test => tests}/test_func.py (100%) rename {pylint/test => tests}/test_functional.py (100%) rename {pylint/test => tests}/test_import_graph.py (100%) rename {pylint/test => tests}/test_regr.py (100%) rename {pylint/test => tests}/test_self.py (94%) rename {pylint/test => tests}/unittest_checker_base.py (100%) rename {pylint/test => tests}/unittest_checker_classes.py (100%) rename {pylint/test => tests}/unittest_checker_exceptions.py (100%) rename {pylint/test => tests}/unittest_checker_format.py (100%) rename {pylint/test => tests}/unittest_checker_imports.py (100%) rename {pylint/test => tests}/unittest_checker_logging.py (100%) rename {pylint/test => tests}/unittest_checker_misc.py (100%) rename {pylint/test => tests}/unittest_checker_python3.py (100%) rename {pylint/test => tests}/unittest_checker_similar.py (100%) rename {pylint/test => tests}/unittest_checker_spelling.py (100%) rename {pylint/test => tests}/unittest_checker_stdlib.py (100%) rename {pylint/test => tests}/unittest_checker_strings.py (100%) rename {pylint/test => tests}/unittest_checker_typecheck.py (100%) rename {pylint/test => tests}/unittest_checker_variables.py (100%) rename {pylint/test => tests}/unittest_checkers_utils.py (100%) rename {pylint/test => tests}/unittest_config.py (100%) rename {pylint/test => tests}/unittest_lint.py (100%) rename {pylint/test => tests}/unittest_pyreverse_diadefs.py (100%) rename {pylint/test => tests}/unittest_pyreverse_inspector.py (98%) rename {pylint/test => tests}/unittest_pyreverse_writer.py (100%) rename {pylint/test => tests}/unittest_reporters_json.py (100%) rename {pylint/test => tests}/unittest_reporting.py (100%) rename {pylint/test => tests}/utils/unittest_ast_walker.py (100%) rename {pylint/test => tests}/utils/unittest_utils.py (100%) diff --git a/MANIFEST.in b/MANIFEST.in index dc97c4ece3..d9c9f63e57 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,11 +1,13 @@ include ChangeLog +include CONTRIBUTORS.txt include COPYING include DEPENDS -include README README.Python3 +include README.rst include bin/* include examples/*.py examples/pylintrc examples/pylintrc_camelcase include elisp/startup elisp/*.el include man/*.1 include pytest.ini -recursive-include doc *.rst *.jpeg Makefile *.html *.py *.bat -graft pylint/test +recursive-include doc *.bat *.png *.py *.svg *.rst *.txt Makefile +graft tests +recursive-exclude tests *.pyc diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index a6752a1783..0cd8303082 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -85,5 +85,3 @@ scripts = [ join("bin", filename) for filename in ("pylint", "symilar", "epylint", "pyreverse") ] - -include_dirs = [join("pylint", "test")] diff --git a/setup.cfg b/setup.cfg index 78cfd76931..0e5b56ccb3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,9 @@ [bdist_rpm] packager = Sylvain Thenault provides = pylint + +[aliases] +test = pytest + +[tool:pytest] +testpaths = tests diff --git a/setup.py b/setup.py index 7d9f883420..bc0619d332 100644 --- a/setup.py +++ b/setup.py @@ -52,7 +52,6 @@ exec(f.read(), __pkginfo__) scripts = __pkginfo__.get("scripts", []) data_files = __pkginfo__.get("data_files", None) -include_dirs = __pkginfo__.get("include_dirs", []) ext_modules = __pkginfo__.get("ext_modules", None) install_requires = __pkginfo__.get("install_requires", None) dependency_links = __pkginfo__.get("dependency_links", []) @@ -97,35 +96,6 @@ def _filter_tests(files): return [f for f in files if testdir not in f] -class MyInstallLib(install_lib.install_lib): - """extend install_lib command to handle package __init__.py and - include_dirs variable if necessary - """ - - def run(self): - """overridden from install_lib class""" - install_lib.install_lib.run(self) - # manually install included directories if any - if include_dirs: - for directory in include_dirs: - dest = join(self.install_dir, directory) - if sys.version_info >= (3, 0): - exclude = {"invalid_encoded_data*", "unknown_encoding*"} - else: - exclude = set() - shutil.rmtree(dest, ignore_errors=True) - shutil.copytree( - directory, dest, ignore=shutil.ignore_patterns(*exclude) - ) - - # override this since pip/easy_install attempt to byte compile test data - # files, some of them being syntactically wrong by design, and this scares - # the end-user - def byte_compile(self, files): - files = _filter_tests(files) - install_lib.install_lib.byte_compile(self, files) - - if easy_install_lib: class easy_install(easy_install_lib.easy_install): @@ -156,7 +126,7 @@ def install(**kwargs): ] } kwargs["packages"] = packages - cmdclass = {"install_lib": MyInstallLib, "build_py": build_py} + cmdclass = {"build_py": build_py} if easy_install_lib: cmdclass["easy_install"] = easy_install return setup( diff --git a/pylint/test/acceptance/test_stdlib.py b/tests/acceptance/test_stdlib.py similarity index 100% rename from pylint/test/acceptance/test_stdlib.py rename to tests/acceptance/test_stdlib.py diff --git a/pylint/test/conftest.py b/tests/conftest.py similarity index 100% rename from pylint/test/conftest.py rename to tests/conftest.py diff --git a/pylint/test/data/__init__.py b/tests/data/__init__.py similarity index 100% rename from pylint/test/data/__init__.py rename to tests/data/__init__.py diff --git a/pylint/test/data/ascript b/tests/data/ascript similarity index 100% rename from pylint/test/data/ascript rename to tests/data/ascript diff --git a/pylint/test/data/classes_No_Name.dot b/tests/data/classes_No_Name.dot similarity index 100% rename from pylint/test/data/classes_No_Name.dot rename to tests/data/classes_No_Name.dot diff --git a/pylint/test/data/clientmodule_test.py b/tests/data/clientmodule_test.py similarity index 100% rename from pylint/test/data/clientmodule_test.py rename to tests/data/clientmodule_test.py diff --git a/pylint/test/data/packages_No_Name.dot b/tests/data/packages_No_Name.dot similarity index 100% rename from pylint/test/data/packages_No_Name.dot rename to tests/data/packages_No_Name.dot diff --git a/pylint/test/data/suppliermodule_test.py b/tests/data/suppliermodule_test.py similarity index 100% rename from pylint/test/data/suppliermodule_test.py rename to tests/data/suppliermodule_test.py diff --git a/pylint/test/extensions/__init__.py b/tests/extensions/__init__.py similarity index 100% rename from pylint/test/extensions/__init__.py rename to tests/extensions/__init__.py diff --git a/pylint/test/extensions/data/bad_builtin.py b/tests/extensions/data/bad_builtin.py similarity index 100% rename from pylint/test/extensions/data/bad_builtin.py rename to tests/extensions/data/bad_builtin.py diff --git a/pylint/test/extensions/data/broad_try_clause.py b/tests/extensions/data/broad_try_clause.py similarity index 100% rename from pylint/test/extensions/data/broad_try_clause.py rename to tests/extensions/data/broad_try_clause.py diff --git a/pylint/test/extensions/data/compare_to_zero.py b/tests/extensions/data/compare_to_zero.py similarity index 100% rename from pylint/test/extensions/data/compare_to_zero.py rename to tests/extensions/data/compare_to_zero.py diff --git a/pylint/test/extensions/data/docstring.py b/tests/extensions/data/docstring.py similarity index 100% rename from pylint/test/extensions/data/docstring.py rename to tests/extensions/data/docstring.py diff --git a/pylint/test/extensions/data/elif.py b/tests/extensions/data/elif.py similarity index 100% rename from pylint/test/extensions/data/elif.py rename to tests/extensions/data/elif.py diff --git a/pylint/test/extensions/data/empty_string_comparison.py b/tests/extensions/data/empty_string_comparison.py similarity index 100% rename from pylint/test/extensions/data/empty_string_comparison.py rename to tests/extensions/data/empty_string_comparison.py diff --git a/pylint/test/extensions/data/mccabe.py b/tests/extensions/data/mccabe.py similarity index 100% rename from pylint/test/extensions/data/mccabe.py rename to tests/extensions/data/mccabe.py diff --git a/pylint/test/extensions/data/overlapping_exceptions.py b/tests/extensions/data/overlapping_exceptions.py similarity index 100% rename from pylint/test/extensions/data/overlapping_exceptions.py rename to tests/extensions/data/overlapping_exceptions.py diff --git a/pylint/test/extensions/data/overlapping_exceptions_py33.py b/tests/extensions/data/overlapping_exceptions_py33.py similarity index 100% rename from pylint/test/extensions/data/overlapping_exceptions_py33.py rename to tests/extensions/data/overlapping_exceptions_py33.py diff --git a/pylint/test/extensions/data/redefined.py b/tests/extensions/data/redefined.py similarity index 100% rename from pylint/test/extensions/data/redefined.py rename to tests/extensions/data/redefined.py diff --git a/pylint/test/extensions/test_bad_builtin.py b/tests/extensions/test_bad_builtin.py similarity index 100% rename from pylint/test/extensions/test_bad_builtin.py rename to tests/extensions/test_bad_builtin.py diff --git a/pylint/test/extensions/test_broad_try_clause.py b/tests/extensions/test_broad_try_clause.py similarity index 100% rename from pylint/test/extensions/test_broad_try_clause.py rename to tests/extensions/test_broad_try_clause.py diff --git a/pylint/test/extensions/test_check_docs.py b/tests/extensions/test_check_docs.py similarity index 100% rename from pylint/test/extensions/test_check_docs.py rename to tests/extensions/test_check_docs.py diff --git a/pylint/test/extensions/test_check_docs_utils.py b/tests/extensions/test_check_docs_utils.py similarity index 100% rename from pylint/test/extensions/test_check_docs_utils.py rename to tests/extensions/test_check_docs_utils.py diff --git a/pylint/test/extensions/test_check_mccabe.py b/tests/extensions/test_check_mccabe.py similarity index 100% rename from pylint/test/extensions/test_check_mccabe.py rename to tests/extensions/test_check_mccabe.py diff --git a/pylint/test/extensions/test_check_raise_docs.py b/tests/extensions/test_check_raise_docs.py similarity index 100% rename from pylint/test/extensions/test_check_raise_docs.py rename to tests/extensions/test_check_raise_docs.py diff --git a/pylint/test/extensions/test_check_return_docs.py b/tests/extensions/test_check_return_docs.py similarity index 100% rename from pylint/test/extensions/test_check_return_docs.py rename to tests/extensions/test_check_return_docs.py diff --git a/pylint/test/extensions/test_check_yields_docs.py b/tests/extensions/test_check_yields_docs.py similarity index 100% rename from pylint/test/extensions/test_check_yields_docs.py rename to tests/extensions/test_check_yields_docs.py diff --git a/pylint/test/extensions/test_comparetozero.py b/tests/extensions/test_comparetozero.py similarity index 100% rename from pylint/test/extensions/test_comparetozero.py rename to tests/extensions/test_comparetozero.py diff --git a/pylint/test/extensions/test_docstyle.py b/tests/extensions/test_docstyle.py similarity index 100% rename from pylint/test/extensions/test_docstyle.py rename to tests/extensions/test_docstyle.py diff --git a/pylint/test/extensions/test_elseif_used.py b/tests/extensions/test_elseif_used.py similarity index 100% rename from pylint/test/extensions/test_elseif_used.py rename to tests/extensions/test_elseif_used.py diff --git a/pylint/test/extensions/test_emptystring.py b/tests/extensions/test_emptystring.py similarity index 100% rename from pylint/test/extensions/test_emptystring.py rename to tests/extensions/test_emptystring.py diff --git a/pylint/test/extensions/test_overlapping_exceptions.py b/tests/extensions/test_overlapping_exceptions.py similarity index 100% rename from pylint/test/extensions/test_overlapping_exceptions.py rename to tests/extensions/test_overlapping_exceptions.py diff --git a/pylint/test/extensions/test_redefined.py b/tests/extensions/test_redefined.py similarity index 100% rename from pylint/test/extensions/test_redefined.py rename to tests/extensions/test_redefined.py diff --git a/pylint/test/functional/__init__.py b/tests/functional/__init__.py similarity index 100% rename from pylint/test/functional/__init__.py rename to tests/functional/__init__.py diff --git a/pylint/test/functional/abstract_abc_methods.py b/tests/functional/abstract_abc_methods.py similarity index 100% rename from pylint/test/functional/abstract_abc_methods.py rename to tests/functional/abstract_abc_methods.py diff --git a/pylint/test/functional/abstract_class_instantiated_in_class.py b/tests/functional/abstract_class_instantiated_in_class.py similarity index 100% rename from pylint/test/functional/abstract_class_instantiated_in_class.py rename to tests/functional/abstract_class_instantiated_in_class.py diff --git a/pylint/test/functional/abstract_class_instantiated_py2.py b/tests/functional/abstract_class_instantiated_py2.py similarity index 100% rename from pylint/test/functional/abstract_class_instantiated_py2.py rename to tests/functional/abstract_class_instantiated_py2.py diff --git a/pylint/test/functional/abstract_class_instantiated_py2.rc b/tests/functional/abstract_class_instantiated_py2.rc similarity index 100% rename from pylint/test/functional/abstract_class_instantiated_py2.rc rename to tests/functional/abstract_class_instantiated_py2.rc diff --git a/pylint/test/functional/abstract_class_instantiated_py2.txt b/tests/functional/abstract_class_instantiated_py2.txt similarity index 100% rename from pylint/test/functional/abstract_class_instantiated_py2.txt rename to tests/functional/abstract_class_instantiated_py2.txt diff --git a/pylint/test/functional/abstract_class_instantiated_py3.py b/tests/functional/abstract_class_instantiated_py3.py similarity index 100% rename from pylint/test/functional/abstract_class_instantiated_py3.py rename to tests/functional/abstract_class_instantiated_py3.py diff --git a/pylint/test/functional/abstract_class_instantiated_py3.rc b/tests/functional/abstract_class_instantiated_py3.rc similarity index 100% rename from pylint/test/functional/abstract_class_instantiated_py3.rc rename to tests/functional/abstract_class_instantiated_py3.rc diff --git a/pylint/test/functional/abstract_class_instantiated_py3.txt b/tests/functional/abstract_class_instantiated_py3.txt similarity index 100% rename from pylint/test/functional/abstract_class_instantiated_py3.txt rename to tests/functional/abstract_class_instantiated_py3.txt diff --git a/pylint/test/functional/abstract_class_instantiated_py34.py b/tests/functional/abstract_class_instantiated_py34.py similarity index 100% rename from pylint/test/functional/abstract_class_instantiated_py34.py rename to tests/functional/abstract_class_instantiated_py34.py diff --git a/pylint/test/functional/abstract_class_instantiated_py34.rc b/tests/functional/abstract_class_instantiated_py34.rc similarity index 100% rename from pylint/test/functional/abstract_class_instantiated_py34.rc rename to tests/functional/abstract_class_instantiated_py34.rc diff --git a/pylint/test/functional/abstract_class_instantiated_py34.txt b/tests/functional/abstract_class_instantiated_py34.txt similarity index 100% rename from pylint/test/functional/abstract_class_instantiated_py34.txt rename to tests/functional/abstract_class_instantiated_py34.txt diff --git a/pylint/test/functional/abstract_method_py2.py b/tests/functional/abstract_method_py2.py similarity index 100% rename from pylint/test/functional/abstract_method_py2.py rename to tests/functional/abstract_method_py2.py diff --git a/pylint/test/functional/abstract_method_py2.rc b/tests/functional/abstract_method_py2.rc similarity index 100% rename from pylint/test/functional/abstract_method_py2.rc rename to tests/functional/abstract_method_py2.rc diff --git a/pylint/test/functional/abstract_method_py2.txt b/tests/functional/abstract_method_py2.txt similarity index 100% rename from pylint/test/functional/abstract_method_py2.txt rename to tests/functional/abstract_method_py2.txt diff --git a/pylint/test/functional/abstract_method_py3.py b/tests/functional/abstract_method_py3.py similarity index 100% rename from pylint/test/functional/abstract_method_py3.py rename to tests/functional/abstract_method_py3.py diff --git a/pylint/test/functional/abstract_method_py3.rc b/tests/functional/abstract_method_py3.rc similarity index 100% rename from pylint/test/functional/abstract_method_py3.rc rename to tests/functional/abstract_method_py3.rc diff --git a/pylint/test/functional/abstract_method_py3.txt b/tests/functional/abstract_method_py3.txt similarity index 100% rename from pylint/test/functional/abstract_method_py3.txt rename to tests/functional/abstract_method_py3.txt diff --git a/pylint/test/functional/access_member_before_definition.py b/tests/functional/access_member_before_definition.py similarity index 100% rename from pylint/test/functional/access_member_before_definition.py rename to tests/functional/access_member_before_definition.py diff --git a/pylint/test/functional/access_member_before_definition.txt b/tests/functional/access_member_before_definition.txt similarity index 100% rename from pylint/test/functional/access_member_before_definition.txt rename to tests/functional/access_member_before_definition.txt diff --git a/pylint/test/functional/access_to__name__.py b/tests/functional/access_to__name__.py similarity index 100% rename from pylint/test/functional/access_to__name__.py rename to tests/functional/access_to__name__.py diff --git a/pylint/test/functional/access_to__name__.txt b/tests/functional/access_to__name__.txt similarity index 100% rename from pylint/test/functional/access_to__name__.txt rename to tests/functional/access_to__name__.txt diff --git a/pylint/test/functional/access_to_protected_members.py b/tests/functional/access_to_protected_members.py similarity index 100% rename from pylint/test/functional/access_to_protected_members.py rename to tests/functional/access_to_protected_members.py diff --git a/pylint/test/functional/access_to_protected_members.txt b/tests/functional/access_to_protected_members.txt similarity index 100% rename from pylint/test/functional/access_to_protected_members.txt rename to tests/functional/access_to_protected_members.txt diff --git a/pylint/test/functional/anomalous_unicode_escape_py2.py b/tests/functional/anomalous_unicode_escape_py2.py similarity index 100% rename from pylint/test/functional/anomalous_unicode_escape_py2.py rename to tests/functional/anomalous_unicode_escape_py2.py diff --git a/pylint/test/functional/anomalous_unicode_escape_py2.rc b/tests/functional/anomalous_unicode_escape_py2.rc similarity index 100% rename from pylint/test/functional/anomalous_unicode_escape_py2.rc rename to tests/functional/anomalous_unicode_escape_py2.rc diff --git a/pylint/test/functional/anomalous_unicode_escape_py2.txt b/tests/functional/anomalous_unicode_escape_py2.txt similarity index 100% rename from pylint/test/functional/anomalous_unicode_escape_py2.txt rename to tests/functional/anomalous_unicode_escape_py2.txt diff --git a/pylint/test/functional/anomalous_unicode_escape_py3.py b/tests/functional/anomalous_unicode_escape_py3.py similarity index 100% rename from pylint/test/functional/anomalous_unicode_escape_py3.py rename to tests/functional/anomalous_unicode_escape_py3.py diff --git a/pylint/test/functional/anomalous_unicode_escape_py3.rc b/tests/functional/anomalous_unicode_escape_py3.rc similarity index 100% rename from pylint/test/functional/anomalous_unicode_escape_py3.rc rename to tests/functional/anomalous_unicode_escape_py3.rc diff --git a/pylint/test/functional/anomalous_unicode_escape_py3.txt b/tests/functional/anomalous_unicode_escape_py3.txt similarity index 100% rename from pylint/test/functional/anomalous_unicode_escape_py3.txt rename to tests/functional/anomalous_unicode_escape_py3.txt diff --git a/pylint/test/functional/arguments.py b/tests/functional/arguments.py similarity index 100% rename from pylint/test/functional/arguments.py rename to tests/functional/arguments.py diff --git a/pylint/test/functional/arguments.rc b/tests/functional/arguments.rc similarity index 100% rename from pylint/test/functional/arguments.rc rename to tests/functional/arguments.rc diff --git a/pylint/test/functional/arguments.txt b/tests/functional/arguments.txt similarity index 100% rename from pylint/test/functional/arguments.txt rename to tests/functional/arguments.txt diff --git a/pylint/test/functional/arguments_differ.py b/tests/functional/arguments_differ.py similarity index 100% rename from pylint/test/functional/arguments_differ.py rename to tests/functional/arguments_differ.py diff --git a/pylint/test/functional/arguments_differ.txt b/tests/functional/arguments_differ.txt similarity index 100% rename from pylint/test/functional/arguments_differ.txt rename to tests/functional/arguments_differ.txt diff --git a/pylint/test/functional/arguments_differ_py3.py b/tests/functional/arguments_differ_py3.py similarity index 100% rename from pylint/test/functional/arguments_differ_py3.py rename to tests/functional/arguments_differ_py3.py diff --git a/pylint/test/functional/arguments_differ_py3.rc b/tests/functional/arguments_differ_py3.rc similarity index 100% rename from pylint/test/functional/arguments_differ_py3.rc rename to tests/functional/arguments_differ_py3.rc diff --git a/pylint/test/functional/arguments_differ_py3.txt b/tests/functional/arguments_differ_py3.txt similarity index 100% rename from pylint/test/functional/arguments_differ_py3.txt rename to tests/functional/arguments_differ_py3.txt diff --git a/pylint/test/functional/assert_on_tuple.py b/tests/functional/assert_on_tuple.py similarity index 100% rename from pylint/test/functional/assert_on_tuple.py rename to tests/functional/assert_on_tuple.py diff --git a/pylint/test/functional/assert_on_tuple.txt b/tests/functional/assert_on_tuple.txt similarity index 100% rename from pylint/test/functional/assert_on_tuple.txt rename to tests/functional/assert_on_tuple.txt diff --git a/pylint/test/functional/assigning_non_slot.py b/tests/functional/assigning_non_slot.py similarity index 100% rename from pylint/test/functional/assigning_non_slot.py rename to tests/functional/assigning_non_slot.py diff --git a/pylint/test/functional/assigning_non_slot.txt b/tests/functional/assigning_non_slot.txt similarity index 100% rename from pylint/test/functional/assigning_non_slot.txt rename to tests/functional/assigning_non_slot.txt diff --git a/pylint/test/functional/assignment_from_no_return.py b/tests/functional/assignment_from_no_return.py similarity index 100% rename from pylint/test/functional/assignment_from_no_return.py rename to tests/functional/assignment_from_no_return.py diff --git a/pylint/test/functional/assignment_from_no_return.txt b/tests/functional/assignment_from_no_return.txt similarity index 100% rename from pylint/test/functional/assignment_from_no_return.txt rename to tests/functional/assignment_from_no_return.txt diff --git a/pylint/test/functional/assignment_from_no_return_py3.py b/tests/functional/assignment_from_no_return_py3.py similarity index 100% rename from pylint/test/functional/assignment_from_no_return_py3.py rename to tests/functional/assignment_from_no_return_py3.py diff --git a/pylint/test/functional/assignment_from_no_return_py3.rc b/tests/functional/assignment_from_no_return_py3.rc similarity index 100% rename from pylint/test/functional/assignment_from_no_return_py3.rc rename to tests/functional/assignment_from_no_return_py3.rc diff --git a/pylint/test/functional/assignment_from_no_return_py3.txt b/tests/functional/assignment_from_no_return_py3.txt similarity index 100% rename from pylint/test/functional/assignment_from_no_return_py3.txt rename to tests/functional/assignment_from_no_return_py3.txt diff --git a/pylint/test/functional/async_functions.py b/tests/functional/async_functions.py similarity index 100% rename from pylint/test/functional/async_functions.py rename to tests/functional/async_functions.py diff --git a/pylint/test/functional/async_functions.rc b/tests/functional/async_functions.rc similarity index 100% rename from pylint/test/functional/async_functions.rc rename to tests/functional/async_functions.rc diff --git a/pylint/test/functional/async_functions.txt b/tests/functional/async_functions.txt similarity index 100% rename from pylint/test/functional/async_functions.txt rename to tests/functional/async_functions.txt diff --git a/pylint/test/functional/attribute_defined_outside_init.py b/tests/functional/attribute_defined_outside_init.py similarity index 100% rename from pylint/test/functional/attribute_defined_outside_init.py rename to tests/functional/attribute_defined_outside_init.py diff --git a/pylint/test/functional/attribute_defined_outside_init.txt b/tests/functional/attribute_defined_outside_init.txt similarity index 100% rename from pylint/test/functional/attribute_defined_outside_init.txt rename to tests/functional/attribute_defined_outside_init.txt diff --git a/pylint/test/functional/bad_continuation.py b/tests/functional/bad_continuation.py similarity index 100% rename from pylint/test/functional/bad_continuation.py rename to tests/functional/bad_continuation.py diff --git a/pylint/test/functional/bad_continuation.txt b/tests/functional/bad_continuation.txt similarity index 100% rename from pylint/test/functional/bad_continuation.txt rename to tests/functional/bad_continuation.txt diff --git a/pylint/test/functional/bad_continuation_py36.py b/tests/functional/bad_continuation_py36.py similarity index 100% rename from pylint/test/functional/bad_continuation_py36.py rename to tests/functional/bad_continuation_py36.py diff --git a/pylint/test/functional/bad_continuation_py36.rc b/tests/functional/bad_continuation_py36.rc similarity index 100% rename from pylint/test/functional/bad_continuation_py36.rc rename to tests/functional/bad_continuation_py36.rc diff --git a/pylint/test/functional/bad_continuation_tabs.py b/tests/functional/bad_continuation_tabs.py similarity index 100% rename from pylint/test/functional/bad_continuation_tabs.py rename to tests/functional/bad_continuation_tabs.py diff --git a/pylint/test/functional/bad_continuation_tabs.rc b/tests/functional/bad_continuation_tabs.rc similarity index 100% rename from pylint/test/functional/bad_continuation_tabs.rc rename to tests/functional/bad_continuation_tabs.rc diff --git a/pylint/test/functional/bad_continuation_tabs.txt b/tests/functional/bad_continuation_tabs.txt similarity index 100% rename from pylint/test/functional/bad_continuation_tabs.txt rename to tests/functional/bad_continuation_tabs.txt diff --git a/pylint/test/functional/bad_except_order.py b/tests/functional/bad_except_order.py similarity index 100% rename from pylint/test/functional/bad_except_order.py rename to tests/functional/bad_except_order.py diff --git a/pylint/test/functional/bad_except_order.txt b/tests/functional/bad_except_order.txt similarity index 100% rename from pylint/test/functional/bad_except_order.txt rename to tests/functional/bad_except_order.txt diff --git a/pylint/test/functional/bad_exception_context.py b/tests/functional/bad_exception_context.py similarity index 100% rename from pylint/test/functional/bad_exception_context.py rename to tests/functional/bad_exception_context.py diff --git a/pylint/test/functional/bad_exception_context.rc b/tests/functional/bad_exception_context.rc similarity index 100% rename from pylint/test/functional/bad_exception_context.rc rename to tests/functional/bad_exception_context.rc diff --git a/pylint/test/functional/bad_exception_context.txt b/tests/functional/bad_exception_context.txt similarity index 100% rename from pylint/test/functional/bad_exception_context.txt rename to tests/functional/bad_exception_context.txt diff --git a/pylint/test/functional/bad_indentation.py b/tests/functional/bad_indentation.py similarity index 100% rename from pylint/test/functional/bad_indentation.py rename to tests/functional/bad_indentation.py diff --git a/pylint/test/functional/bad_indentation.txt b/tests/functional/bad_indentation.txt similarity index 100% rename from pylint/test/functional/bad_indentation.txt rename to tests/functional/bad_indentation.txt diff --git a/pylint/test/functional/bad_inline_option.py b/tests/functional/bad_inline_option.py similarity index 100% rename from pylint/test/functional/bad_inline_option.py rename to tests/functional/bad_inline_option.py diff --git a/pylint/test/functional/bad_inline_option.rc b/tests/functional/bad_inline_option.rc similarity index 100% rename from pylint/test/functional/bad_inline_option.rc rename to tests/functional/bad_inline_option.rc diff --git a/pylint/test/functional/bad_inline_option.txt b/tests/functional/bad_inline_option.txt similarity index 100% rename from pylint/test/functional/bad_inline_option.txt rename to tests/functional/bad_inline_option.txt diff --git a/pylint/test/functional/bad_open_mode.py b/tests/functional/bad_open_mode.py similarity index 100% rename from pylint/test/functional/bad_open_mode.py rename to tests/functional/bad_open_mode.py diff --git a/pylint/test/functional/bad_open_mode.rc b/tests/functional/bad_open_mode.rc similarity index 100% rename from pylint/test/functional/bad_open_mode.rc rename to tests/functional/bad_open_mode.rc diff --git a/pylint/test/functional/bad_open_mode.txt b/tests/functional/bad_open_mode.txt similarity index 100% rename from pylint/test/functional/bad_open_mode.txt rename to tests/functional/bad_open_mode.txt diff --git a/pylint/test/functional/bad_open_mode_py3.py b/tests/functional/bad_open_mode_py3.py similarity index 100% rename from pylint/test/functional/bad_open_mode_py3.py rename to tests/functional/bad_open_mode_py3.py diff --git a/pylint/test/functional/bad_open_mode_py3.rc b/tests/functional/bad_open_mode_py3.rc similarity index 100% rename from pylint/test/functional/bad_open_mode_py3.rc rename to tests/functional/bad_open_mode_py3.rc diff --git a/pylint/test/functional/bad_open_mode_py3.txt b/tests/functional/bad_open_mode_py3.txt similarity index 100% rename from pylint/test/functional/bad_open_mode_py3.txt rename to tests/functional/bad_open_mode_py3.txt diff --git a/pylint/test/functional/bad_reversed_sequence.py b/tests/functional/bad_reversed_sequence.py similarity index 100% rename from pylint/test/functional/bad_reversed_sequence.py rename to tests/functional/bad_reversed_sequence.py diff --git a/pylint/test/functional/bad_reversed_sequence.txt b/tests/functional/bad_reversed_sequence.txt similarity index 100% rename from pylint/test/functional/bad_reversed_sequence.txt rename to tests/functional/bad_reversed_sequence.txt diff --git a/pylint/test/functional/bad_staticmethod_argument.py b/tests/functional/bad_staticmethod_argument.py similarity index 100% rename from pylint/test/functional/bad_staticmethod_argument.py rename to tests/functional/bad_staticmethod_argument.py diff --git a/pylint/test/functional/bad_staticmethod_argument.txt b/tests/functional/bad_staticmethod_argument.txt similarity index 100% rename from pylint/test/functional/bad_staticmethod_argument.txt rename to tests/functional/bad_staticmethod_argument.txt diff --git a/pylint/test/functional/bad_thread_instantiation.py b/tests/functional/bad_thread_instantiation.py similarity index 100% rename from pylint/test/functional/bad_thread_instantiation.py rename to tests/functional/bad_thread_instantiation.py diff --git a/pylint/test/functional/bad_thread_instantiation.txt b/tests/functional/bad_thread_instantiation.txt similarity index 100% rename from pylint/test/functional/bad_thread_instantiation.txt rename to tests/functional/bad_thread_instantiation.txt diff --git a/pylint/test/functional/bad_whitespace.py b/tests/functional/bad_whitespace.py similarity index 100% rename from pylint/test/functional/bad_whitespace.py rename to tests/functional/bad_whitespace.py diff --git a/pylint/test/functional/bad_whitespace.txt b/tests/functional/bad_whitespace.txt similarity index 100% rename from pylint/test/functional/bad_whitespace.txt rename to tests/functional/bad_whitespace.txt diff --git a/pylint/test/functional/bare_except.py b/tests/functional/bare_except.py similarity index 100% rename from pylint/test/functional/bare_except.py rename to tests/functional/bare_except.py diff --git a/pylint/test/functional/bare_except.txt b/tests/functional/bare_except.txt similarity index 100% rename from pylint/test/functional/bare_except.txt rename to tests/functional/bare_except.txt diff --git a/pylint/test/functional/blacklisted_name.py b/tests/functional/blacklisted_name.py similarity index 100% rename from pylint/test/functional/blacklisted_name.py rename to tests/functional/blacklisted_name.py diff --git a/pylint/test/functional/blacklisted_name.txt b/tests/functional/blacklisted_name.txt similarity index 100% rename from pylint/test/functional/blacklisted_name.txt rename to tests/functional/blacklisted_name.txt diff --git a/pylint/test/functional/boolean_datetime.py b/tests/functional/boolean_datetime.py similarity index 100% rename from pylint/test/functional/boolean_datetime.py rename to tests/functional/boolean_datetime.py diff --git a/pylint/test/functional/boolean_datetime.rc b/tests/functional/boolean_datetime.rc similarity index 100% rename from pylint/test/functional/boolean_datetime.rc rename to tests/functional/boolean_datetime.rc diff --git a/pylint/test/functional/boolean_datetime.txt b/tests/functional/boolean_datetime.txt similarity index 100% rename from pylint/test/functional/boolean_datetime.txt rename to tests/functional/boolean_datetime.txt diff --git a/pylint/test/functional/broad_except.py b/tests/functional/broad_except.py similarity index 100% rename from pylint/test/functional/broad_except.py rename to tests/functional/broad_except.py diff --git a/pylint/test/functional/broad_except.txt b/tests/functional/broad_except.txt similarity index 100% rename from pylint/test/functional/broad_except.txt rename to tests/functional/broad_except.txt diff --git a/pylint/test/functional/bugfix_local_scope_metaclass_1177.py b/tests/functional/bugfix_local_scope_metaclass_1177.py similarity index 100% rename from pylint/test/functional/bugfix_local_scope_metaclass_1177.py rename to tests/functional/bugfix_local_scope_metaclass_1177.py diff --git a/pylint/test/functional/bugfix_local_scope_metaclass_1177.rc b/tests/functional/bugfix_local_scope_metaclass_1177.rc similarity index 100% rename from pylint/test/functional/bugfix_local_scope_metaclass_1177.rc rename to tests/functional/bugfix_local_scope_metaclass_1177.rc diff --git a/pylint/test/functional/cellvar_escaping_loop.py b/tests/functional/cellvar_escaping_loop.py similarity index 100% rename from pylint/test/functional/cellvar_escaping_loop.py rename to tests/functional/cellvar_escaping_loop.py diff --git a/pylint/test/functional/cellvar_escaping_loop.txt b/tests/functional/cellvar_escaping_loop.txt similarity index 100% rename from pylint/test/functional/cellvar_escaping_loop.txt rename to tests/functional/cellvar_escaping_loop.txt diff --git a/pylint/test/functional/class_members_py27.py b/tests/functional/class_members_py27.py similarity index 100% rename from pylint/test/functional/class_members_py27.py rename to tests/functional/class_members_py27.py diff --git a/pylint/test/functional/class_members_py27.rc b/tests/functional/class_members_py27.rc similarity index 100% rename from pylint/test/functional/class_members_py27.rc rename to tests/functional/class_members_py27.rc diff --git a/pylint/test/functional/class_members_py27.txt b/tests/functional/class_members_py27.txt similarity index 100% rename from pylint/test/functional/class_members_py27.txt rename to tests/functional/class_members_py27.txt diff --git a/pylint/test/functional/class_members_py30.py b/tests/functional/class_members_py30.py similarity index 100% rename from pylint/test/functional/class_members_py30.py rename to tests/functional/class_members_py30.py diff --git a/pylint/test/functional/class_members_py30.rc b/tests/functional/class_members_py30.rc similarity index 100% rename from pylint/test/functional/class_members_py30.rc rename to tests/functional/class_members_py30.rc diff --git a/pylint/test/functional/class_members_py30.txt b/tests/functional/class_members_py30.txt similarity index 100% rename from pylint/test/functional/class_members_py30.txt rename to tests/functional/class_members_py30.txt diff --git a/pylint/test/functional/class_scope.py b/tests/functional/class_scope.py similarity index 100% rename from pylint/test/functional/class_scope.py rename to tests/functional/class_scope.py diff --git a/pylint/test/functional/class_scope.txt b/tests/functional/class_scope.txt similarity index 100% rename from pylint/test/functional/class_scope.txt rename to tests/functional/class_scope.txt diff --git a/pylint/test/functional/comparison_with_callable.py b/tests/functional/comparison_with_callable.py similarity index 100% rename from pylint/test/functional/comparison_with_callable.py rename to tests/functional/comparison_with_callable.py diff --git a/pylint/test/functional/comparison_with_callable.txt b/tests/functional/comparison_with_callable.txt similarity index 100% rename from pylint/test/functional/comparison_with_callable.txt rename to tests/functional/comparison_with_callable.txt diff --git a/pylint/test/functional/confidence_filter.py b/tests/functional/confidence_filter.py similarity index 100% rename from pylint/test/functional/confidence_filter.py rename to tests/functional/confidence_filter.py diff --git a/pylint/test/functional/confidence_filter.rc b/tests/functional/confidence_filter.rc similarity index 100% rename from pylint/test/functional/confidence_filter.rc rename to tests/functional/confidence_filter.rc diff --git a/pylint/test/functional/confidence_filter.txt b/tests/functional/confidence_filter.txt similarity index 100% rename from pylint/test/functional/confidence_filter.txt rename to tests/functional/confidence_filter.txt diff --git a/pylint/test/functional/confusing_with_statement.py b/tests/functional/confusing_with_statement.py similarity index 100% rename from pylint/test/functional/confusing_with_statement.py rename to tests/functional/confusing_with_statement.py diff --git a/pylint/test/functional/confusing_with_statement.txt b/tests/functional/confusing_with_statement.txt similarity index 100% rename from pylint/test/functional/confusing_with_statement.txt rename to tests/functional/confusing_with_statement.txt diff --git a/pylint/test/functional/consider_iterating_dictionary.py b/tests/functional/consider_iterating_dictionary.py similarity index 100% rename from pylint/test/functional/consider_iterating_dictionary.py rename to tests/functional/consider_iterating_dictionary.py diff --git a/pylint/test/functional/consider_iterating_dictionary.txt b/tests/functional/consider_iterating_dictionary.txt similarity index 100% rename from pylint/test/functional/consider_iterating_dictionary.txt rename to tests/functional/consider_iterating_dictionary.txt diff --git a/pylint/test/functional/consider_join.py b/tests/functional/consider_join.py similarity index 100% rename from pylint/test/functional/consider_join.py rename to tests/functional/consider_join.py diff --git a/pylint/test/functional/consider_join.txt b/tests/functional/consider_join.txt similarity index 100% rename from pylint/test/functional/consider_join.txt rename to tests/functional/consider_join.txt diff --git a/pylint/test/functional/consider_merging_isinstance.py b/tests/functional/consider_merging_isinstance.py similarity index 100% rename from pylint/test/functional/consider_merging_isinstance.py rename to tests/functional/consider_merging_isinstance.py diff --git a/pylint/test/functional/consider_merging_isinstance.txt b/tests/functional/consider_merging_isinstance.txt similarity index 100% rename from pylint/test/functional/consider_merging_isinstance.txt rename to tests/functional/consider_merging_isinstance.txt diff --git a/pylint/test/functional/consider_swap_variables.py b/tests/functional/consider_swap_variables.py similarity index 100% rename from pylint/test/functional/consider_swap_variables.py rename to tests/functional/consider_swap_variables.py diff --git a/pylint/test/functional/consider_swap_variables.txt b/tests/functional/consider_swap_variables.txt similarity index 100% rename from pylint/test/functional/consider_swap_variables.txt rename to tests/functional/consider_swap_variables.txt diff --git a/pylint/test/functional/consider_using_dict_comprehension.py b/tests/functional/consider_using_dict_comprehension.py similarity index 100% rename from pylint/test/functional/consider_using_dict_comprehension.py rename to tests/functional/consider_using_dict_comprehension.py diff --git a/pylint/test/functional/consider_using_dict_comprehension.txt b/tests/functional/consider_using_dict_comprehension.txt similarity index 100% rename from pylint/test/functional/consider_using_dict_comprehension.txt rename to tests/functional/consider_using_dict_comprehension.txt diff --git a/pylint/test/functional/consider_using_enumerate.py b/tests/functional/consider_using_enumerate.py similarity index 100% rename from pylint/test/functional/consider_using_enumerate.py rename to tests/functional/consider_using_enumerate.py diff --git a/pylint/test/functional/consider_using_enumerate.txt b/tests/functional/consider_using_enumerate.txt similarity index 100% rename from pylint/test/functional/consider_using_enumerate.txt rename to tests/functional/consider_using_enumerate.txt diff --git a/pylint/test/functional/consider_using_get.py b/tests/functional/consider_using_get.py similarity index 100% rename from pylint/test/functional/consider_using_get.py rename to tests/functional/consider_using_get.py diff --git a/pylint/test/functional/consider_using_get.txt b/tests/functional/consider_using_get.txt similarity index 100% rename from pylint/test/functional/consider_using_get.txt rename to tests/functional/consider_using_get.txt diff --git a/pylint/test/functional/consider_using_in.py b/tests/functional/consider_using_in.py similarity index 100% rename from pylint/test/functional/consider_using_in.py rename to tests/functional/consider_using_in.py diff --git a/pylint/test/functional/consider_using_in.txt b/tests/functional/consider_using_in.txt similarity index 100% rename from pylint/test/functional/consider_using_in.txt rename to tests/functional/consider_using_in.txt diff --git a/pylint/test/functional/consider_using_set_comprehension.py b/tests/functional/consider_using_set_comprehension.py similarity index 100% rename from pylint/test/functional/consider_using_set_comprehension.py rename to tests/functional/consider_using_set_comprehension.py diff --git a/pylint/test/functional/consider_using_set_comprehension.txt b/tests/functional/consider_using_set_comprehension.txt similarity index 100% rename from pylint/test/functional/consider_using_set_comprehension.txt rename to tests/functional/consider_using_set_comprehension.txt diff --git a/pylint/test/functional/continue_in_finally.py b/tests/functional/continue_in_finally.py similarity index 100% rename from pylint/test/functional/continue_in_finally.py rename to tests/functional/continue_in_finally.py diff --git a/pylint/test/functional/continue_in_finally.txt b/tests/functional/continue_in_finally.txt similarity index 100% rename from pylint/test/functional/continue_in_finally.txt rename to tests/functional/continue_in_finally.txt diff --git a/pylint/test/functional/control_pragmas.py b/tests/functional/control_pragmas.py similarity index 100% rename from pylint/test/functional/control_pragmas.py rename to tests/functional/control_pragmas.py diff --git a/pylint/test/functional/control_pragmas.txt b/tests/functional/control_pragmas.txt similarity index 100% rename from pylint/test/functional/control_pragmas.txt rename to tests/functional/control_pragmas.txt diff --git a/pylint/test/functional/crash_missing_module_type.py b/tests/functional/crash_missing_module_type.py similarity index 100% rename from pylint/test/functional/crash_missing_module_type.py rename to tests/functional/crash_missing_module_type.py diff --git a/pylint/test/functional/crash_missing_module_type.txt b/tests/functional/crash_missing_module_type.txt similarity index 100% rename from pylint/test/functional/crash_missing_module_type.txt rename to tests/functional/crash_missing_module_type.txt diff --git a/pylint/test/functional/ctor_arguments.py b/tests/functional/ctor_arguments.py similarity index 100% rename from pylint/test/functional/ctor_arguments.py rename to tests/functional/ctor_arguments.py diff --git a/pylint/test/functional/ctor_arguments.txt b/tests/functional/ctor_arguments.txt similarity index 100% rename from pylint/test/functional/ctor_arguments.txt rename to tests/functional/ctor_arguments.txt diff --git a/pylint/test/functional/dangerous_default_value.py b/tests/functional/dangerous_default_value.py similarity index 100% rename from pylint/test/functional/dangerous_default_value.py rename to tests/functional/dangerous_default_value.py diff --git a/pylint/test/functional/dangerous_default_value.rc b/tests/functional/dangerous_default_value.rc similarity index 100% rename from pylint/test/functional/dangerous_default_value.rc rename to tests/functional/dangerous_default_value.rc diff --git a/pylint/test/functional/dangerous_default_value.txt b/tests/functional/dangerous_default_value.txt similarity index 100% rename from pylint/test/functional/dangerous_default_value.txt rename to tests/functional/dangerous_default_value.txt diff --git a/pylint/test/functional/dangerous_default_value_py30.py b/tests/functional/dangerous_default_value_py30.py similarity index 100% rename from pylint/test/functional/dangerous_default_value_py30.py rename to tests/functional/dangerous_default_value_py30.py diff --git a/pylint/test/functional/dangerous_default_value_py30.rc b/tests/functional/dangerous_default_value_py30.rc similarity index 100% rename from pylint/test/functional/dangerous_default_value_py30.rc rename to tests/functional/dangerous_default_value_py30.rc diff --git a/pylint/test/functional/dangerous_default_value_py30.txt b/tests/functional/dangerous_default_value_py30.txt similarity index 100% rename from pylint/test/functional/dangerous_default_value_py30.txt rename to tests/functional/dangerous_default_value_py30.txt diff --git a/pylint/test/functional/defined_and_used_on_same_line.py b/tests/functional/defined_and_used_on_same_line.py similarity index 100% rename from pylint/test/functional/defined_and_used_on_same_line.py rename to tests/functional/defined_and_used_on_same_line.py diff --git a/pylint/test/functional/deprecated_lambda.py b/tests/functional/deprecated_lambda.py similarity index 100% rename from pylint/test/functional/deprecated_lambda.py rename to tests/functional/deprecated_lambda.py diff --git a/pylint/test/functional/deprecated_lambda.rc b/tests/functional/deprecated_lambda.rc similarity index 100% rename from pylint/test/functional/deprecated_lambda.rc rename to tests/functional/deprecated_lambda.rc diff --git a/pylint/test/functional/deprecated_lambda.txt b/tests/functional/deprecated_lambda.txt similarity index 100% rename from pylint/test/functional/deprecated_lambda.txt rename to tests/functional/deprecated_lambda.txt diff --git a/pylint/test/functional/deprecated_method_getmoduleinfo.py b/tests/functional/deprecated_method_getmoduleinfo.py similarity index 100% rename from pylint/test/functional/deprecated_method_getmoduleinfo.py rename to tests/functional/deprecated_method_getmoduleinfo.py diff --git a/pylint/test/functional/deprecated_method_getmoduleinfo.rc b/tests/functional/deprecated_method_getmoduleinfo.rc similarity index 100% rename from pylint/test/functional/deprecated_method_getmoduleinfo.rc rename to tests/functional/deprecated_method_getmoduleinfo.rc diff --git a/pylint/test/functional/deprecated_method_getmoduleinfo.txt b/tests/functional/deprecated_method_getmoduleinfo.txt similarity index 100% rename from pylint/test/functional/deprecated_method_getmoduleinfo.txt rename to tests/functional/deprecated_method_getmoduleinfo.txt diff --git a/pylint/test/functional/deprecated_methods_py2.py b/tests/functional/deprecated_methods_py2.py similarity index 100% rename from pylint/test/functional/deprecated_methods_py2.py rename to tests/functional/deprecated_methods_py2.py diff --git a/pylint/test/functional/deprecated_methods_py2.rc b/tests/functional/deprecated_methods_py2.rc similarity index 100% rename from pylint/test/functional/deprecated_methods_py2.rc rename to tests/functional/deprecated_methods_py2.rc diff --git a/pylint/test/functional/deprecated_methods_py2.txt b/tests/functional/deprecated_methods_py2.txt similarity index 100% rename from pylint/test/functional/deprecated_methods_py2.txt rename to tests/functional/deprecated_methods_py2.txt diff --git a/pylint/test/functional/deprecated_methods_py3.py b/tests/functional/deprecated_methods_py3.py similarity index 100% rename from pylint/test/functional/deprecated_methods_py3.py rename to tests/functional/deprecated_methods_py3.py diff --git a/pylint/test/functional/deprecated_methods_py3.rc b/tests/functional/deprecated_methods_py3.rc similarity index 100% rename from pylint/test/functional/deprecated_methods_py3.rc rename to tests/functional/deprecated_methods_py3.rc diff --git a/pylint/test/functional/deprecated_methods_py3.txt b/tests/functional/deprecated_methods_py3.txt similarity index 100% rename from pylint/test/functional/deprecated_methods_py3.txt rename to tests/functional/deprecated_methods_py3.txt diff --git a/pylint/test/functional/deprecated_methods_py36.py b/tests/functional/deprecated_methods_py36.py similarity index 100% rename from pylint/test/functional/deprecated_methods_py36.py rename to tests/functional/deprecated_methods_py36.py diff --git a/pylint/test/functional/deprecated_methods_py36.rc b/tests/functional/deprecated_methods_py36.rc similarity index 100% rename from pylint/test/functional/deprecated_methods_py36.rc rename to tests/functional/deprecated_methods_py36.rc diff --git a/pylint/test/functional/deprecated_methods_py36.txt b/tests/functional/deprecated_methods_py36.txt similarity index 100% rename from pylint/test/functional/deprecated_methods_py36.txt rename to tests/functional/deprecated_methods_py36.txt diff --git a/pylint/test/functional/deprecated_methods_py38.py b/tests/functional/deprecated_methods_py38.py similarity index 100% rename from pylint/test/functional/deprecated_methods_py38.py rename to tests/functional/deprecated_methods_py38.py diff --git a/pylint/test/functional/deprecated_methods_py38.rc b/tests/functional/deprecated_methods_py38.rc similarity index 100% rename from pylint/test/functional/deprecated_methods_py38.rc rename to tests/functional/deprecated_methods_py38.rc diff --git a/pylint/test/functional/deprecated_methods_py38.txt b/tests/functional/deprecated_methods_py38.txt similarity index 100% rename from pylint/test/functional/deprecated_methods_py38.txt rename to tests/functional/deprecated_methods_py38.txt diff --git a/pylint/test/functional/deprecated_module_py2.py b/tests/functional/deprecated_module_py2.py similarity index 100% rename from pylint/test/functional/deprecated_module_py2.py rename to tests/functional/deprecated_module_py2.py diff --git a/pylint/test/functional/deprecated_module_py2.rc b/tests/functional/deprecated_module_py2.rc similarity index 100% rename from pylint/test/functional/deprecated_module_py2.rc rename to tests/functional/deprecated_module_py2.rc diff --git a/pylint/test/functional/deprecated_module_py2.txt b/tests/functional/deprecated_module_py2.txt similarity index 100% rename from pylint/test/functional/deprecated_module_py2.txt rename to tests/functional/deprecated_module_py2.txt diff --git a/pylint/test/functional/deprecated_module_py3.py b/tests/functional/deprecated_module_py3.py similarity index 100% rename from pylint/test/functional/deprecated_module_py3.py rename to tests/functional/deprecated_module_py3.py diff --git a/pylint/test/functional/deprecated_module_py3.rc b/tests/functional/deprecated_module_py3.rc similarity index 100% rename from pylint/test/functional/deprecated_module_py3.rc rename to tests/functional/deprecated_module_py3.rc diff --git a/pylint/test/functional/deprecated_module_py3.txt b/tests/functional/deprecated_module_py3.txt similarity index 100% rename from pylint/test/functional/deprecated_module_py3.txt rename to tests/functional/deprecated_module_py3.txt diff --git a/pylint/test/functional/deprecated_module_py36.py b/tests/functional/deprecated_module_py36.py similarity index 100% rename from pylint/test/functional/deprecated_module_py36.py rename to tests/functional/deprecated_module_py36.py diff --git a/pylint/test/functional/deprecated_module_py36.rc b/tests/functional/deprecated_module_py36.rc similarity index 100% rename from pylint/test/functional/deprecated_module_py36.rc rename to tests/functional/deprecated_module_py36.rc diff --git a/pylint/test/functional/deprecated_module_py36.txt b/tests/functional/deprecated_module_py36.txt similarity index 100% rename from pylint/test/functional/deprecated_module_py36.txt rename to tests/functional/deprecated_module_py36.txt diff --git a/pylint/test/functional/deprecated_module_py4.py b/tests/functional/deprecated_module_py4.py similarity index 100% rename from pylint/test/functional/deprecated_module_py4.py rename to tests/functional/deprecated_module_py4.py diff --git a/pylint/test/functional/deprecated_module_py4.rc b/tests/functional/deprecated_module_py4.rc similarity index 100% rename from pylint/test/functional/deprecated_module_py4.rc rename to tests/functional/deprecated_module_py4.rc diff --git a/pylint/test/functional/deprecated_module_py4.txt b/tests/functional/deprecated_module_py4.txt similarity index 100% rename from pylint/test/functional/deprecated_module_py4.txt rename to tests/functional/deprecated_module_py4.txt diff --git a/pylint/test/functional/deprecated_module_uninstalled.py b/tests/functional/deprecated_module_uninstalled.py similarity index 100% rename from pylint/test/functional/deprecated_module_uninstalled.py rename to tests/functional/deprecated_module_uninstalled.py diff --git a/pylint/test/functional/deprecated_module_uninstalled.rc b/tests/functional/deprecated_module_uninstalled.rc similarity index 100% rename from pylint/test/functional/deprecated_module_uninstalled.rc rename to tests/functional/deprecated_module_uninstalled.rc diff --git a/pylint/test/functional/deprecated_module_uninstalled.txt b/tests/functional/deprecated_module_uninstalled.txt similarity index 100% rename from pylint/test/functional/deprecated_module_uninstalled.txt rename to tests/functional/deprecated_module_uninstalled.txt diff --git a/pylint/test/functional/dict_iter_missing_items.py b/tests/functional/dict_iter_missing_items.py similarity index 100% rename from pylint/test/functional/dict_iter_missing_items.py rename to tests/functional/dict_iter_missing_items.py diff --git a/pylint/test/functional/dict_iter_missing_items.txt b/tests/functional/dict_iter_missing_items.txt similarity index 100% rename from pylint/test/functional/dict_iter_missing_items.txt rename to tests/functional/dict_iter_missing_items.txt diff --git a/pylint/test/functional/disable_msg_github_issue_1389.py b/tests/functional/disable_msg_github_issue_1389.py similarity index 100% rename from pylint/test/functional/disable_msg_github_issue_1389.py rename to tests/functional/disable_msg_github_issue_1389.py diff --git a/pylint/test/functional/disable_msg_github_issue_1389.rc b/tests/functional/disable_msg_github_issue_1389.rc similarity index 100% rename from pylint/test/functional/disable_msg_github_issue_1389.rc rename to tests/functional/disable_msg_github_issue_1389.rc diff --git a/pylint/test/functional/disable_ungrouped_imports.py b/tests/functional/disable_ungrouped_imports.py similarity index 100% rename from pylint/test/functional/disable_ungrouped_imports.py rename to tests/functional/disable_ungrouped_imports.py diff --git a/pylint/test/functional/disable_ungrouped_imports.txt b/tests/functional/disable_ungrouped_imports.txt similarity index 100% rename from pylint/test/functional/disable_ungrouped_imports.txt rename to tests/functional/disable_ungrouped_imports.txt diff --git a/pylint/test/functional/disable_wrong_import_order.py b/tests/functional/disable_wrong_import_order.py similarity index 100% rename from pylint/test/functional/disable_wrong_import_order.py rename to tests/functional/disable_wrong_import_order.py diff --git a/pylint/test/functional/disable_wrong_import_order.txt b/tests/functional/disable_wrong_import_order.txt similarity index 100% rename from pylint/test/functional/disable_wrong_import_order.txt rename to tests/functional/disable_wrong_import_order.txt diff --git a/pylint/test/functional/disable_wrong_import_position.py b/tests/functional/disable_wrong_import_position.py similarity index 100% rename from pylint/test/functional/disable_wrong_import_position.py rename to tests/functional/disable_wrong_import_position.py diff --git a/pylint/test/functional/docstrings.py b/tests/functional/docstrings.py similarity index 100% rename from pylint/test/functional/docstrings.py rename to tests/functional/docstrings.py diff --git a/pylint/test/functional/docstrings.txt b/tests/functional/docstrings.txt similarity index 100% rename from pylint/test/functional/docstrings.txt rename to tests/functional/docstrings.txt diff --git a/pylint/test/functional/duplicate_argument_name.py b/tests/functional/duplicate_argument_name.py similarity index 100% rename from pylint/test/functional/duplicate_argument_name.py rename to tests/functional/duplicate_argument_name.py diff --git a/pylint/test/functional/duplicate_argument_name.txt b/tests/functional/duplicate_argument_name.txt similarity index 100% rename from pylint/test/functional/duplicate_argument_name.txt rename to tests/functional/duplicate_argument_name.txt diff --git a/pylint/test/functional/duplicate_argument_name_py3.py b/tests/functional/duplicate_argument_name_py3.py similarity index 100% rename from pylint/test/functional/duplicate_argument_name_py3.py rename to tests/functional/duplicate_argument_name_py3.py diff --git a/pylint/test/functional/duplicate_argument_name_py3.rc b/tests/functional/duplicate_argument_name_py3.rc similarity index 100% rename from pylint/test/functional/duplicate_argument_name_py3.rc rename to tests/functional/duplicate_argument_name_py3.rc diff --git a/pylint/test/functional/duplicate_argument_name_py3.txt b/tests/functional/duplicate_argument_name_py3.txt similarity index 100% rename from pylint/test/functional/duplicate_argument_name_py3.txt rename to tests/functional/duplicate_argument_name_py3.txt diff --git a/pylint/test/functional/duplicate_bases.py b/tests/functional/duplicate_bases.py similarity index 100% rename from pylint/test/functional/duplicate_bases.py rename to tests/functional/duplicate_bases.py diff --git a/pylint/test/functional/duplicate_bases.txt b/tests/functional/duplicate_bases.txt similarity index 100% rename from pylint/test/functional/duplicate_bases.txt rename to tests/functional/duplicate_bases.txt diff --git a/pylint/test/functional/duplicate_dict_literal_key.py b/tests/functional/duplicate_dict_literal_key.py similarity index 100% rename from pylint/test/functional/duplicate_dict_literal_key.py rename to tests/functional/duplicate_dict_literal_key.py diff --git a/pylint/test/functional/duplicate_dict_literal_key.txt b/tests/functional/duplicate_dict_literal_key.txt similarity index 100% rename from pylint/test/functional/duplicate_dict_literal_key.txt rename to tests/functional/duplicate_dict_literal_key.txt diff --git a/pylint/test/functional/duplicate_except.py b/tests/functional/duplicate_except.py similarity index 100% rename from pylint/test/functional/duplicate_except.py rename to tests/functional/duplicate_except.py diff --git a/pylint/test/functional/duplicate_except.txt b/tests/functional/duplicate_except.txt similarity index 100% rename from pylint/test/functional/duplicate_except.txt rename to tests/functional/duplicate_except.txt diff --git a/pylint/test/functional/duplicate_string_formatting_argument.py b/tests/functional/duplicate_string_formatting_argument.py similarity index 100% rename from pylint/test/functional/duplicate_string_formatting_argument.py rename to tests/functional/duplicate_string_formatting_argument.py diff --git a/pylint/test/functional/duplicate_string_formatting_argument.txt b/tests/functional/duplicate_string_formatting_argument.txt similarity index 100% rename from pylint/test/functional/duplicate_string_formatting_argument.txt rename to tests/functional/duplicate_string_formatting_argument.txt diff --git a/pylint/test/functional/eval_used.py b/tests/functional/eval_used.py similarity index 100% rename from pylint/test/functional/eval_used.py rename to tests/functional/eval_used.py diff --git a/pylint/test/functional/eval_used.txt b/tests/functional/eval_used.txt similarity index 100% rename from pylint/test/functional/eval_used.txt rename to tests/functional/eval_used.txt diff --git a/pylint/test/functional/exception_is_binary_op.py b/tests/functional/exception_is_binary_op.py similarity index 100% rename from pylint/test/functional/exception_is_binary_op.py rename to tests/functional/exception_is_binary_op.py diff --git a/pylint/test/functional/exception_is_binary_op.txt b/tests/functional/exception_is_binary_op.txt similarity index 100% rename from pylint/test/functional/exception_is_binary_op.txt rename to tests/functional/exception_is_binary_op.txt diff --git a/pylint/test/functional/exception_message.py b/tests/functional/exception_message.py similarity index 100% rename from pylint/test/functional/exception_message.py rename to tests/functional/exception_message.py diff --git a/pylint/test/functional/exception_message.rc b/tests/functional/exception_message.rc similarity index 100% rename from pylint/test/functional/exception_message.rc rename to tests/functional/exception_message.rc diff --git a/pylint/test/functional/exception_message.txt b/tests/functional/exception_message.txt similarity index 100% rename from pylint/test/functional/exception_message.txt rename to tests/functional/exception_message.txt diff --git a/pylint/test/functional/exec_used_py2.py b/tests/functional/exec_used_py2.py similarity index 100% rename from pylint/test/functional/exec_used_py2.py rename to tests/functional/exec_used_py2.py diff --git a/pylint/test/functional/exec_used_py2.rc b/tests/functional/exec_used_py2.rc similarity index 100% rename from pylint/test/functional/exec_used_py2.rc rename to tests/functional/exec_used_py2.rc diff --git a/pylint/test/functional/exec_used_py2.txt b/tests/functional/exec_used_py2.txt similarity index 100% rename from pylint/test/functional/exec_used_py2.txt rename to tests/functional/exec_used_py2.txt diff --git a/pylint/test/functional/exec_used_py3.py b/tests/functional/exec_used_py3.py similarity index 100% rename from pylint/test/functional/exec_used_py3.py rename to tests/functional/exec_used_py3.py diff --git a/pylint/test/functional/exec_used_py3.rc b/tests/functional/exec_used_py3.rc similarity index 100% rename from pylint/test/functional/exec_used_py3.rc rename to tests/functional/exec_used_py3.rc diff --git a/pylint/test/functional/exec_used_py3.txt b/tests/functional/exec_used_py3.txt similarity index 100% rename from pylint/test/functional/exec_used_py3.txt rename to tests/functional/exec_used_py3.txt diff --git a/pylint/test/functional/fallback_import_disabled.py b/tests/functional/fallback_import_disabled.py similarity index 100% rename from pylint/test/functional/fallback_import_disabled.py rename to tests/functional/fallback_import_disabled.py diff --git a/pylint/test/functional/fallback_import_disabled.txt b/tests/functional/fallback_import_disabled.txt similarity index 100% rename from pylint/test/functional/fallback_import_disabled.txt rename to tests/functional/fallback_import_disabled.txt diff --git a/pylint/test/functional/fallback_import_enabled.py b/tests/functional/fallback_import_enabled.py similarity index 100% rename from pylint/test/functional/fallback_import_enabled.py rename to tests/functional/fallback_import_enabled.py diff --git a/pylint/test/functional/fallback_import_enabled.rc b/tests/functional/fallback_import_enabled.rc similarity index 100% rename from pylint/test/functional/fallback_import_enabled.rc rename to tests/functional/fallback_import_enabled.rc diff --git a/pylint/test/functional/fallback_import_enabled.txt b/tests/functional/fallback_import_enabled.txt similarity index 100% rename from pylint/test/functional/fallback_import_enabled.txt rename to tests/functional/fallback_import_enabled.txt diff --git a/pylint/test/functional/fixme.py b/tests/functional/fixme.py similarity index 100% rename from pylint/test/functional/fixme.py rename to tests/functional/fixme.py diff --git a/pylint/test/functional/fixme.txt b/tests/functional/fixme.txt similarity index 100% rename from pylint/test/functional/fixme.txt rename to tests/functional/fixme.txt diff --git a/pylint/test/functional/fixme_bad_formatting_1139.py b/tests/functional/fixme_bad_formatting_1139.py similarity index 100% rename from pylint/test/functional/fixme_bad_formatting_1139.py rename to tests/functional/fixme_bad_formatting_1139.py diff --git a/pylint/test/functional/fixme_bad_formatting_1139.rc b/tests/functional/fixme_bad_formatting_1139.rc similarity index 100% rename from pylint/test/functional/fixme_bad_formatting_1139.rc rename to tests/functional/fixme_bad_formatting_1139.rc diff --git a/pylint/test/functional/fixme_bad_formatting_1139.txt b/tests/functional/fixme_bad_formatting_1139.txt similarity index 100% rename from pylint/test/functional/fixme_bad_formatting_1139.txt rename to tests/functional/fixme_bad_formatting_1139.txt diff --git a/pylint/test/functional/formatted_string_literal_with_if_py36.py b/tests/functional/formatted_string_literal_with_if_py36.py similarity index 100% rename from pylint/test/functional/formatted_string_literal_with_if_py36.py rename to tests/functional/formatted_string_literal_with_if_py36.py diff --git a/pylint/test/functional/formatted_string_literal_with_if_py36.rc b/tests/functional/formatted_string_literal_with_if_py36.rc similarity index 100% rename from pylint/test/functional/formatted_string_literal_with_if_py36.rc rename to tests/functional/formatted_string_literal_with_if_py36.rc diff --git a/pylint/test/functional/formatting.txt b/tests/functional/formatting.txt similarity index 100% rename from pylint/test/functional/formatting.txt rename to tests/functional/formatting.txt diff --git a/pylint/test/functional/function_redefined.py b/tests/functional/function_redefined.py similarity index 100% rename from pylint/test/functional/function_redefined.py rename to tests/functional/function_redefined.py diff --git a/pylint/test/functional/function_redefined.txt b/tests/functional/function_redefined.txt similarity index 100% rename from pylint/test/functional/function_redefined.txt rename to tests/functional/function_redefined.txt diff --git a/pylint/test/functional/future_import.py b/tests/functional/future_import.py similarity index 100% rename from pylint/test/functional/future_import.py rename to tests/functional/future_import.py diff --git a/pylint/test/functional/future_unicode_literals.py b/tests/functional/future_unicode_literals.py similarity index 100% rename from pylint/test/functional/future_unicode_literals.py rename to tests/functional/future_unicode_literals.py diff --git a/pylint/test/functional/future_unicode_literals.rc b/tests/functional/future_unicode_literals.rc similarity index 100% rename from pylint/test/functional/future_unicode_literals.rc rename to tests/functional/future_unicode_literals.rc diff --git a/pylint/test/functional/future_unicode_literals.txt b/tests/functional/future_unicode_literals.txt similarity index 100% rename from pylint/test/functional/future_unicode_literals.txt rename to tests/functional/future_unicode_literals.txt diff --git a/pylint/test/functional/generated_members.py b/tests/functional/generated_members.py similarity index 100% rename from pylint/test/functional/generated_members.py rename to tests/functional/generated_members.py diff --git a/pylint/test/functional/generated_members.rc b/tests/functional/generated_members.rc similarity index 100% rename from pylint/test/functional/generated_members.rc rename to tests/functional/generated_members.rc diff --git a/pylint/test/functional/genexp_in_class_scope.py b/tests/functional/genexp_in_class_scope.py similarity index 100% rename from pylint/test/functional/genexp_in_class_scope.py rename to tests/functional/genexp_in_class_scope.py diff --git a/pylint/test/functional/genexp_in_class_scope.txt b/tests/functional/genexp_in_class_scope.txt similarity index 100% rename from pylint/test/functional/genexp_in_class_scope.txt rename to tests/functional/genexp_in_class_scope.txt diff --git a/pylint/test/functional/genexpr_variable_scope.py b/tests/functional/genexpr_variable_scope.py similarity index 100% rename from pylint/test/functional/genexpr_variable_scope.py rename to tests/functional/genexpr_variable_scope.py diff --git a/pylint/test/functional/genexpr_variable_scope.txt b/tests/functional/genexpr_variable_scope.txt similarity index 100% rename from pylint/test/functional/genexpr_variable_scope.txt rename to tests/functional/genexpr_variable_scope.txt diff --git a/pylint/test/functional/globals.py b/tests/functional/globals.py similarity index 100% rename from pylint/test/functional/globals.py rename to tests/functional/globals.py diff --git a/pylint/test/functional/globals.txt b/tests/functional/globals.txt similarity index 100% rename from pylint/test/functional/globals.txt rename to tests/functional/globals.txt diff --git a/pylint/test/functional/implicit_str_concat_in_sequence.py b/tests/functional/implicit_str_concat_in_sequence.py similarity index 100% rename from pylint/test/functional/implicit_str_concat_in_sequence.py rename to tests/functional/implicit_str_concat_in_sequence.py diff --git a/pylint/test/functional/implicit_str_concat_in_sequence.txt b/tests/functional/implicit_str_concat_in_sequence.txt similarity index 100% rename from pylint/test/functional/implicit_str_concat_in_sequence.txt rename to tests/functional/implicit_str_concat_in_sequence.txt diff --git a/pylint/test/functional/implicit_str_concat_in_sequence_latin1.py b/tests/functional/implicit_str_concat_in_sequence_latin1.py similarity index 100% rename from pylint/test/functional/implicit_str_concat_in_sequence_latin1.py rename to tests/functional/implicit_str_concat_in_sequence_latin1.py diff --git a/pylint/test/functional/implicit_str_concat_in_sequence_latin1.txt b/tests/functional/implicit_str_concat_in_sequence_latin1.txt similarity index 100% rename from pylint/test/functional/implicit_str_concat_in_sequence_latin1.txt rename to tests/functional/implicit_str_concat_in_sequence_latin1.txt diff --git a/pylint/test/functional/implicit_str_concat_in_sequence_multiline.py b/tests/functional/implicit_str_concat_in_sequence_multiline.py similarity index 100% rename from pylint/test/functional/implicit_str_concat_in_sequence_multiline.py rename to tests/functional/implicit_str_concat_in_sequence_multiline.py diff --git a/pylint/test/functional/implicit_str_concat_in_sequence_multiline.rc b/tests/functional/implicit_str_concat_in_sequence_multiline.rc similarity index 100% rename from pylint/test/functional/implicit_str_concat_in_sequence_multiline.rc rename to tests/functional/implicit_str_concat_in_sequence_multiline.rc diff --git a/pylint/test/functional/implicit_str_concat_in_sequence_multiline.txt b/tests/functional/implicit_str_concat_in_sequence_multiline.txt similarity index 100% rename from pylint/test/functional/implicit_str_concat_in_sequence_multiline.txt rename to tests/functional/implicit_str_concat_in_sequence_multiline.txt diff --git a/pylint/test/functional/implicit_str_concat_in_sequence_utf8.py b/tests/functional/implicit_str_concat_in_sequence_utf8.py similarity index 100% rename from pylint/test/functional/implicit_str_concat_in_sequence_utf8.py rename to tests/functional/implicit_str_concat_in_sequence_utf8.py diff --git a/pylint/test/functional/implicit_str_concat_in_sequence_utf8.txt b/tests/functional/implicit_str_concat_in_sequence_utf8.txt similarity index 100% rename from pylint/test/functional/implicit_str_concat_in_sequence_utf8.txt rename to tests/functional/implicit_str_concat_in_sequence_utf8.txt diff --git a/pylint/test/functional/import_error.py b/tests/functional/import_error.py similarity index 100% rename from pylint/test/functional/import_error.py rename to tests/functional/import_error.py diff --git a/pylint/test/functional/import_error.rc b/tests/functional/import_error.rc similarity index 100% rename from pylint/test/functional/import_error.rc rename to tests/functional/import_error.rc diff --git a/pylint/test/functional/import_error.txt b/tests/functional/import_error.txt similarity index 100% rename from pylint/test/functional/import_error.txt rename to tests/functional/import_error.txt diff --git a/pylint/test/functional/inconsistent_mro.py b/tests/functional/inconsistent_mro.py similarity index 100% rename from pylint/test/functional/inconsistent_mro.py rename to tests/functional/inconsistent_mro.py diff --git a/pylint/test/functional/inconsistent_mro.txt b/tests/functional/inconsistent_mro.txt similarity index 100% rename from pylint/test/functional/inconsistent_mro.txt rename to tests/functional/inconsistent_mro.txt diff --git a/pylint/test/functional/inconsistent_returns.py b/tests/functional/inconsistent_returns.py similarity index 100% rename from pylint/test/functional/inconsistent_returns.py rename to tests/functional/inconsistent_returns.py diff --git a/pylint/test/functional/inconsistent_returns.rc b/tests/functional/inconsistent_returns.rc similarity index 100% rename from pylint/test/functional/inconsistent_returns.rc rename to tests/functional/inconsistent_returns.rc diff --git a/pylint/test/functional/inconsistent_returns.txt b/tests/functional/inconsistent_returns.txt similarity index 100% rename from pylint/test/functional/inconsistent_returns.txt rename to tests/functional/inconsistent_returns.txt diff --git a/pylint/test/functional/indexing_exception.py b/tests/functional/indexing_exception.py similarity index 100% rename from pylint/test/functional/indexing_exception.py rename to tests/functional/indexing_exception.py diff --git a/pylint/test/functional/indexing_exception.rc b/tests/functional/indexing_exception.rc similarity index 100% rename from pylint/test/functional/indexing_exception.rc rename to tests/functional/indexing_exception.rc diff --git a/pylint/test/functional/indexing_exception.txt b/tests/functional/indexing_exception.txt similarity index 100% rename from pylint/test/functional/indexing_exception.txt rename to tests/functional/indexing_exception.txt diff --git a/pylint/test/functional/inherit_non_class.py b/tests/functional/inherit_non_class.py similarity index 100% rename from pylint/test/functional/inherit_non_class.py rename to tests/functional/inherit_non_class.py diff --git a/pylint/test/functional/inherit_non_class.txt b/tests/functional/inherit_non_class.txt similarity index 100% rename from pylint/test/functional/inherit_non_class.txt rename to tests/functional/inherit_non_class.txt diff --git a/pylint/test/functional/init_is_generator.py b/tests/functional/init_is_generator.py similarity index 100% rename from pylint/test/functional/init_is_generator.py rename to tests/functional/init_is_generator.py diff --git a/pylint/test/functional/init_is_generator.txt b/tests/functional/init_is_generator.txt similarity index 100% rename from pylint/test/functional/init_is_generator.txt rename to tests/functional/init_is_generator.txt diff --git a/pylint/test/functional/init_not_called.py b/tests/functional/init_not_called.py similarity index 100% rename from pylint/test/functional/init_not_called.py rename to tests/functional/init_not_called.py diff --git a/pylint/test/functional/init_not_called.txt b/tests/functional/init_not_called.txt similarity index 100% rename from pylint/test/functional/init_not_called.txt rename to tests/functional/init_not_called.txt diff --git a/pylint/test/functional/init_subclass_classmethod_py36.py b/tests/functional/init_subclass_classmethod_py36.py similarity index 100% rename from pylint/test/functional/init_subclass_classmethod_py36.py rename to tests/functional/init_subclass_classmethod_py36.py diff --git a/pylint/test/functional/init_subclass_classmethod_py36.rc b/tests/functional/init_subclass_classmethod_py36.rc similarity index 100% rename from pylint/test/functional/init_subclass_classmethod_py36.rc rename to tests/functional/init_subclass_classmethod_py36.rc diff --git a/pylint/test/functional/invalid_all_object.py b/tests/functional/invalid_all_object.py similarity index 100% rename from pylint/test/functional/invalid_all_object.py rename to tests/functional/invalid_all_object.py diff --git a/pylint/test/functional/invalid_all_object.txt b/tests/functional/invalid_all_object.txt similarity index 100% rename from pylint/test/functional/invalid_all_object.txt rename to tests/functional/invalid_all_object.txt diff --git a/pylint/test/functional/invalid_encoded_data.py b/tests/functional/invalid_encoded_data.py similarity index 100% rename from pylint/test/functional/invalid_encoded_data.py rename to tests/functional/invalid_encoded_data.py diff --git a/pylint/test/functional/invalid_encoded_data.rc b/tests/functional/invalid_encoded_data.rc similarity index 100% rename from pylint/test/functional/invalid_encoded_data.rc rename to tests/functional/invalid_encoded_data.rc diff --git a/pylint/test/functional/invalid_encoded_data.txt b/tests/functional/invalid_encoded_data.txt similarity index 100% rename from pylint/test/functional/invalid_encoded_data.txt rename to tests/functional/invalid_encoded_data.txt diff --git a/pylint/test/functional/invalid_encoding_py27.py b/tests/functional/invalid_encoding_py27.py similarity index 100% rename from pylint/test/functional/invalid_encoding_py27.py rename to tests/functional/invalid_encoding_py27.py diff --git a/pylint/test/functional/invalid_encoding_py27.rc b/tests/functional/invalid_encoding_py27.rc similarity index 100% rename from pylint/test/functional/invalid_encoding_py27.rc rename to tests/functional/invalid_encoding_py27.rc diff --git a/pylint/test/functional/invalid_encoding_py27.txt b/tests/functional/invalid_encoding_py27.txt similarity index 100% rename from pylint/test/functional/invalid_encoding_py27.txt rename to tests/functional/invalid_encoding_py27.txt diff --git a/pylint/test/functional/invalid_envvar_value.py b/tests/functional/invalid_envvar_value.py similarity index 100% rename from pylint/test/functional/invalid_envvar_value.py rename to tests/functional/invalid_envvar_value.py diff --git a/pylint/test/functional/invalid_envvar_value.txt b/tests/functional/invalid_envvar_value.txt similarity index 100% rename from pylint/test/functional/invalid_envvar_value.txt rename to tests/functional/invalid_envvar_value.txt diff --git a/pylint/test/functional/invalid_exceptions_caught.py b/tests/functional/invalid_exceptions_caught.py similarity index 100% rename from pylint/test/functional/invalid_exceptions_caught.py rename to tests/functional/invalid_exceptions_caught.py diff --git a/pylint/test/functional/invalid_exceptions_caught.txt b/tests/functional/invalid_exceptions_caught.txt similarity index 100% rename from pylint/test/functional/invalid_exceptions_caught.txt rename to tests/functional/invalid_exceptions_caught.txt diff --git a/pylint/test/functional/invalid_exceptions_raised.py b/tests/functional/invalid_exceptions_raised.py similarity index 100% rename from pylint/test/functional/invalid_exceptions_raised.py rename to tests/functional/invalid_exceptions_raised.py diff --git a/pylint/test/functional/invalid_exceptions_raised.txt b/tests/functional/invalid_exceptions_raised.txt similarity index 100% rename from pylint/test/functional/invalid_exceptions_raised.txt rename to tests/functional/invalid_exceptions_raised.txt diff --git a/pylint/test/functional/invalid_length_returned.py b/tests/functional/invalid_length_returned.py similarity index 100% rename from pylint/test/functional/invalid_length_returned.py rename to tests/functional/invalid_length_returned.py diff --git a/pylint/test/functional/invalid_length_returned.txt b/tests/functional/invalid_length_returned.txt similarity index 100% rename from pylint/test/functional/invalid_length_returned.txt rename to tests/functional/invalid_length_returned.txt diff --git a/pylint/test/functional/invalid_metaclass.py b/tests/functional/invalid_metaclass.py similarity index 100% rename from pylint/test/functional/invalid_metaclass.py rename to tests/functional/invalid_metaclass.py diff --git a/pylint/test/functional/invalid_metaclass.txt b/tests/functional/invalid_metaclass.txt similarity index 100% rename from pylint/test/functional/invalid_metaclass.txt rename to tests/functional/invalid_metaclass.txt diff --git a/pylint/test/functional/invalid_metaclass_py3.py b/tests/functional/invalid_metaclass_py3.py similarity index 100% rename from pylint/test/functional/invalid_metaclass_py3.py rename to tests/functional/invalid_metaclass_py3.py diff --git a/pylint/test/functional/invalid_metaclass_py3.rc b/tests/functional/invalid_metaclass_py3.rc similarity index 100% rename from pylint/test/functional/invalid_metaclass_py3.rc rename to tests/functional/invalid_metaclass_py3.rc diff --git a/pylint/test/functional/invalid_metaclass_py3.txt b/tests/functional/invalid_metaclass_py3.txt similarity index 100% rename from pylint/test/functional/invalid_metaclass_py3.txt rename to tests/functional/invalid_metaclass_py3.txt diff --git a/pylint/test/functional/invalid_name.py b/tests/functional/invalid_name.py similarity index 100% rename from pylint/test/functional/invalid_name.py rename to tests/functional/invalid_name.py diff --git a/pylint/test/functional/invalid_name.txt b/tests/functional/invalid_name.txt similarity index 100% rename from pylint/test/functional/invalid_name.txt rename to tests/functional/invalid_name.txt diff --git a/pylint/test/functional/invalid_sequence_index.py b/tests/functional/invalid_sequence_index.py similarity index 100% rename from pylint/test/functional/invalid_sequence_index.py rename to tests/functional/invalid_sequence_index.py diff --git a/pylint/test/functional/invalid_sequence_index.txt b/tests/functional/invalid_sequence_index.txt similarity index 100% rename from pylint/test/functional/invalid_sequence_index.txt rename to tests/functional/invalid_sequence_index.txt diff --git a/pylint/test/functional/invalid_slice_index.py b/tests/functional/invalid_slice_index.py similarity index 100% rename from pylint/test/functional/invalid_slice_index.py rename to tests/functional/invalid_slice_index.py diff --git a/pylint/test/functional/invalid_slice_index.txt b/tests/functional/invalid_slice_index.txt similarity index 100% rename from pylint/test/functional/invalid_slice_index.txt rename to tests/functional/invalid_slice_index.txt diff --git a/pylint/test/functional/invalid_star_assignment_target.py b/tests/functional/invalid_star_assignment_target.py similarity index 100% rename from pylint/test/functional/invalid_star_assignment_target.py rename to tests/functional/invalid_star_assignment_target.py diff --git a/pylint/test/functional/invalid_star_assignment_target.rc b/tests/functional/invalid_star_assignment_target.rc similarity index 100% rename from pylint/test/functional/invalid_star_assignment_target.rc rename to tests/functional/invalid_star_assignment_target.rc diff --git a/pylint/test/functional/invalid_star_assignment_target.txt b/tests/functional/invalid_star_assignment_target.txt similarity index 100% rename from pylint/test/functional/invalid_star_assignment_target.txt rename to tests/functional/invalid_star_assignment_target.txt diff --git a/pylint/test/functional/invalid_unary_operand_type.py b/tests/functional/invalid_unary_operand_type.py similarity index 100% rename from pylint/test/functional/invalid_unary_operand_type.py rename to tests/functional/invalid_unary_operand_type.py diff --git a/pylint/test/functional/invalid_unary_operand_type.txt b/tests/functional/invalid_unary_operand_type.txt similarity index 100% rename from pylint/test/functional/invalid_unary_operand_type.txt rename to tests/functional/invalid_unary_operand_type.txt diff --git a/pylint/test/functional/iterable_context.py b/tests/functional/iterable_context.py similarity index 100% rename from pylint/test/functional/iterable_context.py rename to tests/functional/iterable_context.py diff --git a/pylint/test/functional/iterable_context.txt b/tests/functional/iterable_context.txt similarity index 100% rename from pylint/test/functional/iterable_context.txt rename to tests/functional/iterable_context.txt diff --git a/pylint/test/functional/iterable_context_py2.py b/tests/functional/iterable_context_py2.py similarity index 100% rename from pylint/test/functional/iterable_context_py2.py rename to tests/functional/iterable_context_py2.py diff --git a/pylint/test/functional/iterable_context_py2.rc b/tests/functional/iterable_context_py2.rc similarity index 100% rename from pylint/test/functional/iterable_context_py2.rc rename to tests/functional/iterable_context_py2.rc diff --git a/pylint/test/functional/iterable_context_py2.txt b/tests/functional/iterable_context_py2.txt similarity index 100% rename from pylint/test/functional/iterable_context_py2.txt rename to tests/functional/iterable_context_py2.txt diff --git a/pylint/test/functional/iterable_context_py3.py b/tests/functional/iterable_context_py3.py similarity index 100% rename from pylint/test/functional/iterable_context_py3.py rename to tests/functional/iterable_context_py3.py diff --git a/pylint/test/functional/iterable_context_py3.rc b/tests/functional/iterable_context_py3.rc similarity index 100% rename from pylint/test/functional/iterable_context_py3.rc rename to tests/functional/iterable_context_py3.rc diff --git a/pylint/test/functional/iterable_context_py3.txt b/tests/functional/iterable_context_py3.txt similarity index 100% rename from pylint/test/functional/iterable_context_py3.txt rename to tests/functional/iterable_context_py3.txt diff --git a/pylint/test/functional/iterable_context_py36.py b/tests/functional/iterable_context_py36.py similarity index 100% rename from pylint/test/functional/iterable_context_py36.py rename to tests/functional/iterable_context_py36.py diff --git a/pylint/test/functional/iterable_context_py36.rc b/tests/functional/iterable_context_py36.rc similarity index 100% rename from pylint/test/functional/iterable_context_py36.rc rename to tests/functional/iterable_context_py36.rc diff --git a/pylint/test/functional/iterable_context_py36.txt b/tests/functional/iterable_context_py36.txt similarity index 100% rename from pylint/test/functional/iterable_context_py36.txt rename to tests/functional/iterable_context_py36.txt diff --git a/pylint/test/functional/keyword_arg_before_vararg.py b/tests/functional/keyword_arg_before_vararg.py similarity index 100% rename from pylint/test/functional/keyword_arg_before_vararg.py rename to tests/functional/keyword_arg_before_vararg.py diff --git a/pylint/test/functional/keyword_arg_before_vararg.txt b/tests/functional/keyword_arg_before_vararg.txt similarity index 100% rename from pylint/test/functional/keyword_arg_before_vararg.txt rename to tests/functional/keyword_arg_before_vararg.txt diff --git a/pylint/test/functional/len_checks.py b/tests/functional/len_checks.py similarity index 100% rename from pylint/test/functional/len_checks.py rename to tests/functional/len_checks.py diff --git a/pylint/test/functional/len_checks.txt b/tests/functional/len_checks.txt similarity index 100% rename from pylint/test/functional/len_checks.txt rename to tests/functional/len_checks.txt diff --git a/pylint/test/functional/line_endings.py b/tests/functional/line_endings.py similarity index 100% rename from pylint/test/functional/line_endings.py rename to tests/functional/line_endings.py diff --git a/pylint/test/functional/line_endings.rc b/tests/functional/line_endings.rc similarity index 100% rename from pylint/test/functional/line_endings.rc rename to tests/functional/line_endings.rc diff --git a/pylint/test/functional/line_endings.txt b/tests/functional/line_endings.txt similarity index 100% rename from pylint/test/functional/line_endings.txt rename to tests/functional/line_endings.txt diff --git a/pylint/test/functional/line_too_long.py b/tests/functional/line_too_long.py similarity index 100% rename from pylint/test/functional/line_too_long.py rename to tests/functional/line_too_long.py diff --git a/pylint/test/functional/line_too_long.txt b/tests/functional/line_too_long.txt similarity index 100% rename from pylint/test/functional/line_too_long.txt rename to tests/functional/line_too_long.txt diff --git a/pylint/test/functional/line_too_long_end_of_module.py b/tests/functional/line_too_long_end_of_module.py similarity index 100% rename from pylint/test/functional/line_too_long_end_of_module.py rename to tests/functional/line_too_long_end_of_module.py diff --git a/pylint/test/functional/literal_comparison.py b/tests/functional/literal_comparison.py similarity index 100% rename from pylint/test/functional/literal_comparison.py rename to tests/functional/literal_comparison.py diff --git a/pylint/test/functional/literal_comparison.txt b/tests/functional/literal_comparison.txt similarity index 100% rename from pylint/test/functional/literal_comparison.txt rename to tests/functional/literal_comparison.txt diff --git a/pylint/test/functional/logging_format_interpolation.py b/tests/functional/logging_format_interpolation.py similarity index 100% rename from pylint/test/functional/logging_format_interpolation.py rename to tests/functional/logging_format_interpolation.py diff --git a/pylint/test/functional/logging_format_interpolation.txt b/tests/functional/logging_format_interpolation.txt similarity index 100% rename from pylint/test/functional/logging_format_interpolation.txt rename to tests/functional/logging_format_interpolation.txt diff --git a/pylint/test/functional/logging_format_interpolation_py36.py b/tests/functional/logging_format_interpolation_py36.py similarity index 100% rename from pylint/test/functional/logging_format_interpolation_py36.py rename to tests/functional/logging_format_interpolation_py36.py diff --git a/pylint/test/functional/logging_format_interpolation_py36.rc b/tests/functional/logging_format_interpolation_py36.rc similarity index 100% rename from pylint/test/functional/logging_format_interpolation_py36.rc rename to tests/functional/logging_format_interpolation_py36.rc diff --git a/pylint/test/functional/logging_format_interpolation_py36.txt b/tests/functional/logging_format_interpolation_py36.txt similarity index 100% rename from pylint/test/functional/logging_format_interpolation_py36.txt rename to tests/functional/logging_format_interpolation_py36.txt diff --git a/pylint/test/functional/logging_fstring_interpolation_py36.py b/tests/functional/logging_fstring_interpolation_py36.py similarity index 100% rename from pylint/test/functional/logging_fstring_interpolation_py36.py rename to tests/functional/logging_fstring_interpolation_py36.py diff --git a/pylint/test/functional/logging_fstring_interpolation_py36.rc b/tests/functional/logging_fstring_interpolation_py36.rc similarity index 100% rename from pylint/test/functional/logging_fstring_interpolation_py36.rc rename to tests/functional/logging_fstring_interpolation_py36.rc diff --git a/pylint/test/functional/logging_fstring_interpolation_py36.txt b/tests/functional/logging_fstring_interpolation_py36.txt similarity index 100% rename from pylint/test/functional/logging_fstring_interpolation_py36.txt rename to tests/functional/logging_fstring_interpolation_py36.txt diff --git a/pylint/test/functional/logging_not_lazy.py b/tests/functional/logging_not_lazy.py similarity index 100% rename from pylint/test/functional/logging_not_lazy.py rename to tests/functional/logging_not_lazy.py diff --git a/pylint/test/functional/logging_not_lazy.txt b/tests/functional/logging_not_lazy.txt similarity index 100% rename from pylint/test/functional/logging_not_lazy.txt rename to tests/functional/logging_not_lazy.txt diff --git a/pylint/test/functional/logical_tautology.py b/tests/functional/logical_tautology.py similarity index 100% rename from pylint/test/functional/logical_tautology.py rename to tests/functional/logical_tautology.py diff --git a/pylint/test/functional/logical_tautology.txt b/tests/functional/logical_tautology.txt similarity index 100% rename from pylint/test/functional/logical_tautology.txt rename to tests/functional/logical_tautology.txt diff --git a/pylint/test/functional/long_lines_with_utf8.py b/tests/functional/long_lines_with_utf8.py similarity index 100% rename from pylint/test/functional/long_lines_with_utf8.py rename to tests/functional/long_lines_with_utf8.py diff --git a/pylint/test/functional/long_lines_with_utf8.txt b/tests/functional/long_lines_with_utf8.txt similarity index 100% rename from pylint/test/functional/long_lines_with_utf8.txt rename to tests/functional/long_lines_with_utf8.txt diff --git a/pylint/test/functional/long_utf8_lines.py b/tests/functional/long_utf8_lines.py similarity index 100% rename from pylint/test/functional/long_utf8_lines.py rename to tests/functional/long_utf8_lines.py diff --git a/pylint/test/functional/long_utf8_lines.txt b/tests/functional/long_utf8_lines.txt similarity index 100% rename from pylint/test/functional/long_utf8_lines.txt rename to tests/functional/long_utf8_lines.txt diff --git a/pylint/test/functional/lost_exception.py b/tests/functional/lost_exception.py similarity index 100% rename from pylint/test/functional/lost_exception.py rename to tests/functional/lost_exception.py diff --git a/pylint/test/functional/lost_exception.txt b/tests/functional/lost_exception.txt similarity index 100% rename from pylint/test/functional/lost_exception.txt rename to tests/functional/lost_exception.txt diff --git a/pylint/test/functional/mapping_context.py b/tests/functional/mapping_context.py similarity index 100% rename from pylint/test/functional/mapping_context.py rename to tests/functional/mapping_context.py diff --git a/pylint/test/functional/mapping_context.txt b/tests/functional/mapping_context.txt similarity index 100% rename from pylint/test/functional/mapping_context.txt rename to tests/functional/mapping_context.txt diff --git a/pylint/test/functional/mapping_context_py2.py b/tests/functional/mapping_context_py2.py similarity index 100% rename from pylint/test/functional/mapping_context_py2.py rename to tests/functional/mapping_context_py2.py diff --git a/pylint/test/functional/mapping_context_py2.rc b/tests/functional/mapping_context_py2.rc similarity index 100% rename from pylint/test/functional/mapping_context_py2.rc rename to tests/functional/mapping_context_py2.rc diff --git a/pylint/test/functional/mapping_context_py2.txt b/tests/functional/mapping_context_py2.txt similarity index 100% rename from pylint/test/functional/mapping_context_py2.txt rename to tests/functional/mapping_context_py2.txt diff --git a/pylint/test/functional/mapping_context_py3.py b/tests/functional/mapping_context_py3.py similarity index 100% rename from pylint/test/functional/mapping_context_py3.py rename to tests/functional/mapping_context_py3.py diff --git a/pylint/test/functional/mapping_context_py3.rc b/tests/functional/mapping_context_py3.rc similarity index 100% rename from pylint/test/functional/mapping_context_py3.rc rename to tests/functional/mapping_context_py3.rc diff --git a/pylint/test/functional/mapping_context_py3.txt b/tests/functional/mapping_context_py3.txt similarity index 100% rename from pylint/test/functional/mapping_context_py3.txt rename to tests/functional/mapping_context_py3.txt diff --git a/pylint/test/functional/member_checks.py b/tests/functional/member_checks.py similarity index 100% rename from pylint/test/functional/member_checks.py rename to tests/functional/member_checks.py diff --git a/pylint/test/functional/member_checks.txt b/tests/functional/member_checks.txt similarity index 100% rename from pylint/test/functional/member_checks.txt rename to tests/functional/member_checks.txt diff --git a/pylint/test/functional/member_checks_hints.py b/tests/functional/member_checks_hints.py similarity index 100% rename from pylint/test/functional/member_checks_hints.py rename to tests/functional/member_checks_hints.py diff --git a/pylint/test/functional/member_checks_hints.rc b/tests/functional/member_checks_hints.rc similarity index 100% rename from pylint/test/functional/member_checks_hints.rc rename to tests/functional/member_checks_hints.rc diff --git a/pylint/test/functional/member_checks_hints.txt b/tests/functional/member_checks_hints.txt similarity index 100% rename from pylint/test/functional/member_checks_hints.txt rename to tests/functional/member_checks_hints.txt diff --git a/pylint/test/functional/member_checks_ignore_none.py b/tests/functional/member_checks_ignore_none.py similarity index 100% rename from pylint/test/functional/member_checks_ignore_none.py rename to tests/functional/member_checks_ignore_none.py diff --git a/pylint/test/functional/member_checks_ignore_none.rc b/tests/functional/member_checks_ignore_none.rc similarity index 100% rename from pylint/test/functional/member_checks_ignore_none.rc rename to tests/functional/member_checks_ignore_none.rc diff --git a/pylint/test/functional/member_checks_ignore_none.txt b/tests/functional/member_checks_ignore_none.txt similarity index 100% rename from pylint/test/functional/member_checks_ignore_none.txt rename to tests/functional/member_checks_ignore_none.txt diff --git a/pylint/test/functional/member_checks_inference_improvements.py b/tests/functional/member_checks_inference_improvements.py similarity index 100% rename from pylint/test/functional/member_checks_inference_improvements.py rename to tests/functional/member_checks_inference_improvements.py diff --git a/pylint/test/functional/member_checks_no_hints.py b/tests/functional/member_checks_no_hints.py similarity index 100% rename from pylint/test/functional/member_checks_no_hints.py rename to tests/functional/member_checks_no_hints.py diff --git a/pylint/test/functional/member_checks_no_hints.rc b/tests/functional/member_checks_no_hints.rc similarity index 100% rename from pylint/test/functional/member_checks_no_hints.rc rename to tests/functional/member_checks_no_hints.rc diff --git a/pylint/test/functional/member_checks_no_hints.txt b/tests/functional/member_checks_no_hints.txt similarity index 100% rename from pylint/test/functional/member_checks_no_hints.txt rename to tests/functional/member_checks_no_hints.txt diff --git a/pylint/test/functional/member_checks_opaque.py b/tests/functional/member_checks_opaque.py similarity index 100% rename from pylint/test/functional/member_checks_opaque.py rename to tests/functional/member_checks_opaque.py diff --git a/pylint/test/functional/member_checks_opaque.rc b/tests/functional/member_checks_opaque.rc similarity index 100% rename from pylint/test/functional/member_checks_opaque.rc rename to tests/functional/member_checks_opaque.rc diff --git a/pylint/test/functional/member_checks_opaque.txt b/tests/functional/member_checks_opaque.txt similarity index 100% rename from pylint/test/functional/member_checks_opaque.txt rename to tests/functional/member_checks_opaque.txt diff --git a/pylint/test/functional/member_checks_py37.py b/tests/functional/member_checks_py37.py similarity index 100% rename from pylint/test/functional/member_checks_py37.py rename to tests/functional/member_checks_py37.py diff --git a/pylint/test/functional/member_checks_py37.rc b/tests/functional/member_checks_py37.rc similarity index 100% rename from pylint/test/functional/member_checks_py37.rc rename to tests/functional/member_checks_py37.rc diff --git a/pylint/test/functional/member_checks_py37.txt b/tests/functional/member_checks_py37.txt similarity index 100% rename from pylint/test/functional/member_checks_py37.txt rename to tests/functional/member_checks_py37.txt diff --git a/pylint/test/functional/membership_protocol.py b/tests/functional/membership_protocol.py similarity index 100% rename from pylint/test/functional/membership_protocol.py rename to tests/functional/membership_protocol.py diff --git a/pylint/test/functional/membership_protocol.txt b/tests/functional/membership_protocol.txt similarity index 100% rename from pylint/test/functional/membership_protocol.txt rename to tests/functional/membership_protocol.txt diff --git a/pylint/test/functional/membership_protocol_py2.py b/tests/functional/membership_protocol_py2.py similarity index 100% rename from pylint/test/functional/membership_protocol_py2.py rename to tests/functional/membership_protocol_py2.py diff --git a/pylint/test/functional/membership_protocol_py2.rc b/tests/functional/membership_protocol_py2.rc similarity index 100% rename from pylint/test/functional/membership_protocol_py2.rc rename to tests/functional/membership_protocol_py2.rc diff --git a/pylint/test/functional/membership_protocol_py2.txt b/tests/functional/membership_protocol_py2.txt similarity index 100% rename from pylint/test/functional/membership_protocol_py2.txt rename to tests/functional/membership_protocol_py2.txt diff --git a/pylint/test/functional/membership_protocol_py3.py b/tests/functional/membership_protocol_py3.py similarity index 100% rename from pylint/test/functional/membership_protocol_py3.py rename to tests/functional/membership_protocol_py3.py diff --git a/pylint/test/functional/membership_protocol_py3.rc b/tests/functional/membership_protocol_py3.rc similarity index 100% rename from pylint/test/functional/membership_protocol_py3.rc rename to tests/functional/membership_protocol_py3.rc diff --git a/pylint/test/functional/membership_protocol_py3.txt b/tests/functional/membership_protocol_py3.txt similarity index 100% rename from pylint/test/functional/membership_protocol_py3.txt rename to tests/functional/membership_protocol_py3.txt diff --git a/pylint/test/functional/messages_managed_by_id.py b/tests/functional/messages_managed_by_id.py similarity index 100% rename from pylint/test/functional/messages_managed_by_id.py rename to tests/functional/messages_managed_by_id.py diff --git a/pylint/test/functional/messages_managed_by_id.txt b/tests/functional/messages_managed_by_id.txt similarity index 100% rename from pylint/test/functional/messages_managed_by_id.txt rename to tests/functional/messages_managed_by_id.txt diff --git a/pylint/test/functional/method_hidden.py b/tests/functional/method_hidden.py similarity index 100% rename from pylint/test/functional/method_hidden.py rename to tests/functional/method_hidden.py diff --git a/pylint/test/functional/method_hidden.txt b/tests/functional/method_hidden.txt similarity index 100% rename from pylint/test/functional/method_hidden.txt rename to tests/functional/method_hidden.txt diff --git a/pylint/test/functional/misplaced_bare_raise.py b/tests/functional/misplaced_bare_raise.py similarity index 100% rename from pylint/test/functional/misplaced_bare_raise.py rename to tests/functional/misplaced_bare_raise.py diff --git a/pylint/test/functional/misplaced_bare_raise.txt b/tests/functional/misplaced_bare_raise.txt similarity index 100% rename from pylint/test/functional/misplaced_bare_raise.txt rename to tests/functional/misplaced_bare_raise.txt diff --git a/pylint/test/functional/misplaced_comparison_constant.py b/tests/functional/misplaced_comparison_constant.py similarity index 100% rename from pylint/test/functional/misplaced_comparison_constant.py rename to tests/functional/misplaced_comparison_constant.py diff --git a/pylint/test/functional/misplaced_comparison_constant.txt b/tests/functional/misplaced_comparison_constant.txt similarity index 100% rename from pylint/test/functional/misplaced_comparison_constant.txt rename to tests/functional/misplaced_comparison_constant.txt diff --git a/pylint/test/functional/misplaced_format_function.py b/tests/functional/misplaced_format_function.py similarity index 100% rename from pylint/test/functional/misplaced_format_function.py rename to tests/functional/misplaced_format_function.py diff --git a/pylint/test/functional/misplaced_format_function.txt b/tests/functional/misplaced_format_function.txt similarity index 100% rename from pylint/test/functional/misplaced_format_function.txt rename to tests/functional/misplaced_format_function.txt diff --git a/pylint/test/functional/misplaced_future.py b/tests/functional/misplaced_future.py similarity index 100% rename from pylint/test/functional/misplaced_future.py rename to tests/functional/misplaced_future.py diff --git a/pylint/test/functional/misplaced_future.txt b/tests/functional/misplaced_future.txt similarity index 100% rename from pylint/test/functional/misplaced_future.txt rename to tests/functional/misplaced_future.txt diff --git a/pylint/test/functional/missing_docstring.py b/tests/functional/missing_docstring.py similarity index 100% rename from pylint/test/functional/missing_docstring.py rename to tests/functional/missing_docstring.py diff --git a/pylint/test/functional/missing_docstring.txt b/tests/functional/missing_docstring.txt similarity index 100% rename from pylint/test/functional/missing_docstring.txt rename to tests/functional/missing_docstring.txt diff --git a/pylint/test/functional/missing_final_newline.py b/tests/functional/missing_final_newline.py similarity index 100% rename from pylint/test/functional/missing_final_newline.py rename to tests/functional/missing_final_newline.py diff --git a/pylint/test/functional/missing_final_newline.txt b/tests/functional/missing_final_newline.txt similarity index 100% rename from pylint/test/functional/missing_final_newline.txt rename to tests/functional/missing_final_newline.txt diff --git a/pylint/test/functional/missing_kwoa_py3.py b/tests/functional/missing_kwoa_py3.py similarity index 100% rename from pylint/test/functional/missing_kwoa_py3.py rename to tests/functional/missing_kwoa_py3.py diff --git a/pylint/test/functional/missing_kwoa_py3.rc b/tests/functional/missing_kwoa_py3.rc similarity index 100% rename from pylint/test/functional/missing_kwoa_py3.rc rename to tests/functional/missing_kwoa_py3.rc diff --git a/pylint/test/functional/missing_kwoa_py3.txt b/tests/functional/missing_kwoa_py3.txt similarity index 100% rename from pylint/test/functional/missing_kwoa_py3.txt rename to tests/functional/missing_kwoa_py3.txt diff --git a/pylint/test/functional/missing_parentheses_for_call_in_test.py b/tests/functional/missing_parentheses_for_call_in_test.py similarity index 100% rename from pylint/test/functional/missing_parentheses_for_call_in_test.py rename to tests/functional/missing_parentheses_for_call_in_test.py diff --git a/pylint/test/functional/missing_parentheses_for_call_in_test.txt b/tests/functional/missing_parentheses_for_call_in_test.txt similarity index 100% rename from pylint/test/functional/missing_parentheses_for_call_in_test.txt rename to tests/functional/missing_parentheses_for_call_in_test.txt diff --git a/pylint/test/functional/missing_self_argument.py b/tests/functional/missing_self_argument.py similarity index 100% rename from pylint/test/functional/missing_self_argument.py rename to tests/functional/missing_self_argument.py diff --git a/pylint/test/functional/missing_self_argument.txt b/tests/functional/missing_self_argument.txt similarity index 100% rename from pylint/test/functional/missing_self_argument.txt rename to tests/functional/missing_self_argument.txt diff --git a/pylint/test/functional/mixed_indentation.py b/tests/functional/mixed_indentation.py similarity index 100% rename from pylint/test/functional/mixed_indentation.py rename to tests/functional/mixed_indentation.py diff --git a/pylint/test/functional/mixed_indentation.txt b/tests/functional/mixed_indentation.txt similarity index 100% rename from pylint/test/functional/mixed_indentation.txt rename to tests/functional/mixed_indentation.txt diff --git a/pylint/test/functional/monkeypatch_method.py b/tests/functional/monkeypatch_method.py similarity index 100% rename from pylint/test/functional/monkeypatch_method.py rename to tests/functional/monkeypatch_method.py diff --git a/pylint/test/functional/monkeypatch_method.txt b/tests/functional/monkeypatch_method.txt similarity index 100% rename from pylint/test/functional/monkeypatch_method.txt rename to tests/functional/monkeypatch_method.txt diff --git a/pylint/test/functional/multiple_imports.py b/tests/functional/multiple_imports.py similarity index 100% rename from pylint/test/functional/multiple_imports.py rename to tests/functional/multiple_imports.py diff --git a/pylint/test/functional/multiple_imports.txt b/tests/functional/multiple_imports.txt similarity index 100% rename from pylint/test/functional/multiple_imports.txt rename to tests/functional/multiple_imports.txt diff --git a/pylint/test/functional/namePresetCamelCase.py b/tests/functional/namePresetCamelCase.py similarity index 100% rename from pylint/test/functional/namePresetCamelCase.py rename to tests/functional/namePresetCamelCase.py diff --git a/pylint/test/functional/namePresetCamelCase.rc b/tests/functional/namePresetCamelCase.rc similarity index 100% rename from pylint/test/functional/namePresetCamelCase.rc rename to tests/functional/namePresetCamelCase.rc diff --git a/pylint/test/functional/namePresetCamelCase.txt b/tests/functional/namePresetCamelCase.txt similarity index 100% rename from pylint/test/functional/namePresetCamelCase.txt rename to tests/functional/namePresetCamelCase.txt diff --git a/pylint/test/functional/name_preset_snake_case.py b/tests/functional/name_preset_snake_case.py similarity index 100% rename from pylint/test/functional/name_preset_snake_case.py rename to tests/functional/name_preset_snake_case.py diff --git a/pylint/test/functional/name_preset_snake_case.rc b/tests/functional/name_preset_snake_case.rc similarity index 100% rename from pylint/test/functional/name_preset_snake_case.rc rename to tests/functional/name_preset_snake_case.rc diff --git a/pylint/test/functional/name_preset_snake_case.txt b/tests/functional/name_preset_snake_case.txt similarity index 100% rename from pylint/test/functional/name_preset_snake_case.txt rename to tests/functional/name_preset_snake_case.txt diff --git a/pylint/test/functional/name_styles.py b/tests/functional/name_styles.py similarity index 100% rename from pylint/test/functional/name_styles.py rename to tests/functional/name_styles.py diff --git a/pylint/test/functional/name_styles.rc b/tests/functional/name_styles.rc similarity index 100% rename from pylint/test/functional/name_styles.rc rename to tests/functional/name_styles.rc diff --git a/pylint/test/functional/name_styles.txt b/tests/functional/name_styles.txt similarity index 100% rename from pylint/test/functional/name_styles.txt rename to tests/functional/name_styles.txt diff --git a/pylint/test/functional/namedtuple_member_inference.py b/tests/functional/namedtuple_member_inference.py similarity index 100% rename from pylint/test/functional/namedtuple_member_inference.py rename to tests/functional/namedtuple_member_inference.py diff --git a/pylint/test/functional/namedtuple_member_inference.txt b/tests/functional/namedtuple_member_inference.txt similarity index 100% rename from pylint/test/functional/namedtuple_member_inference.txt rename to tests/functional/namedtuple_member_inference.txt diff --git a/pylint/test/functional/names_in__all__.py b/tests/functional/names_in__all__.py similarity index 100% rename from pylint/test/functional/names_in__all__.py rename to tests/functional/names_in__all__.py diff --git a/pylint/test/functional/names_in__all__.txt b/tests/functional/names_in__all__.txt similarity index 100% rename from pylint/test/functional/names_in__all__.txt rename to tests/functional/names_in__all__.txt diff --git a/pylint/test/functional/nested_blocks_issue1088.py b/tests/functional/nested_blocks_issue1088.py similarity index 100% rename from pylint/test/functional/nested_blocks_issue1088.py rename to tests/functional/nested_blocks_issue1088.py diff --git a/pylint/test/functional/nested_blocks_issue1088.txt b/tests/functional/nested_blocks_issue1088.txt similarity index 100% rename from pylint/test/functional/nested_blocks_issue1088.txt rename to tests/functional/nested_blocks_issue1088.txt diff --git a/pylint/test/functional/nested_func_defined_in_loop.py b/tests/functional/nested_func_defined_in_loop.py similarity index 100% rename from pylint/test/functional/nested_func_defined_in_loop.py rename to tests/functional/nested_func_defined_in_loop.py diff --git a/pylint/test/functional/no_classmethod_decorator.py b/tests/functional/no_classmethod_decorator.py similarity index 100% rename from pylint/test/functional/no_classmethod_decorator.py rename to tests/functional/no_classmethod_decorator.py diff --git a/pylint/test/functional/no_classmethod_decorator.txt b/tests/functional/no_classmethod_decorator.txt similarity index 100% rename from pylint/test/functional/no_classmethod_decorator.txt rename to tests/functional/no_classmethod_decorator.txt diff --git a/pylint/test/functional/no_else_raise.py b/tests/functional/no_else_raise.py similarity index 100% rename from pylint/test/functional/no_else_raise.py rename to tests/functional/no_else_raise.py diff --git a/pylint/test/functional/no_else_raise.txt b/tests/functional/no_else_raise.txt similarity index 100% rename from pylint/test/functional/no_else_raise.txt rename to tests/functional/no_else_raise.txt diff --git a/pylint/test/functional/no_else_return.py b/tests/functional/no_else_return.py similarity index 100% rename from pylint/test/functional/no_else_return.py rename to tests/functional/no_else_return.py diff --git a/pylint/test/functional/no_else_return.txt b/tests/functional/no_else_return.txt similarity index 100% rename from pylint/test/functional/no_else_return.txt rename to tests/functional/no_else_return.txt diff --git a/pylint/test/functional/no_name_in_module.py b/tests/functional/no_name_in_module.py similarity index 100% rename from pylint/test/functional/no_name_in_module.py rename to tests/functional/no_name_in_module.py diff --git a/pylint/test/functional/no_name_in_module.txt b/tests/functional/no_name_in_module.txt similarity index 100% rename from pylint/test/functional/no_name_in_module.txt rename to tests/functional/no_name_in_module.txt diff --git a/pylint/test/functional/no_self_argument_py37.py b/tests/functional/no_self_argument_py37.py similarity index 100% rename from pylint/test/functional/no_self_argument_py37.py rename to tests/functional/no_self_argument_py37.py diff --git a/pylint/test/functional/no_self_argument_py37.rc b/tests/functional/no_self_argument_py37.rc similarity index 100% rename from pylint/test/functional/no_self_argument_py37.rc rename to tests/functional/no_self_argument_py37.rc diff --git a/pylint/test/functional/no_self_argument_py37.txt b/tests/functional/no_self_argument_py37.txt similarity index 100% rename from pylint/test/functional/no_self_argument_py37.txt rename to tests/functional/no_self_argument_py37.txt diff --git a/pylint/test/functional/no_self_use.py b/tests/functional/no_self_use.py similarity index 100% rename from pylint/test/functional/no_self_use.py rename to tests/functional/no_self_use.py diff --git a/pylint/test/functional/no_self_use.txt b/tests/functional/no_self_use.txt similarity index 100% rename from pylint/test/functional/no_self_use.txt rename to tests/functional/no_self_use.txt diff --git a/pylint/test/functional/no_self_use_py3.py b/tests/functional/no_self_use_py3.py similarity index 100% rename from pylint/test/functional/no_self_use_py3.py rename to tests/functional/no_self_use_py3.py diff --git a/pylint/test/functional/no_self_use_py3.rc b/tests/functional/no_self_use_py3.rc similarity index 100% rename from pylint/test/functional/no_self_use_py3.rc rename to tests/functional/no_self_use_py3.rc diff --git a/pylint/test/functional/no_self_use_py3.txt b/tests/functional/no_self_use_py3.txt similarity index 100% rename from pylint/test/functional/no_self_use_py3.txt rename to tests/functional/no_self_use_py3.txt diff --git a/pylint/test/functional/no_staticmethod_decorator.py b/tests/functional/no_staticmethod_decorator.py similarity index 100% rename from pylint/test/functional/no_staticmethod_decorator.py rename to tests/functional/no_staticmethod_decorator.py diff --git a/pylint/test/functional/no_staticmethod_decorator.txt b/tests/functional/no_staticmethod_decorator.txt similarity index 100% rename from pylint/test/functional/no_staticmethod_decorator.txt rename to tests/functional/no_staticmethod_decorator.txt diff --git a/pylint/test/functional/non_iterator_returned.py b/tests/functional/non_iterator_returned.py similarity index 100% rename from pylint/test/functional/non_iterator_returned.py rename to tests/functional/non_iterator_returned.py diff --git a/pylint/test/functional/non_iterator_returned.txt b/tests/functional/non_iterator_returned.txt similarity index 100% rename from pylint/test/functional/non_iterator_returned.txt rename to tests/functional/non_iterator_returned.txt diff --git a/pylint/test/functional/none_dunder_protocols_py36.py b/tests/functional/none_dunder_protocols_py36.py similarity index 100% rename from pylint/test/functional/none_dunder_protocols_py36.py rename to tests/functional/none_dunder_protocols_py36.py diff --git a/pylint/test/functional/none_dunder_protocols_py36.rc b/tests/functional/none_dunder_protocols_py36.rc similarity index 100% rename from pylint/test/functional/none_dunder_protocols_py36.rc rename to tests/functional/none_dunder_protocols_py36.rc diff --git a/pylint/test/functional/none_dunder_protocols_py36.txt b/tests/functional/none_dunder_protocols_py36.txt similarity index 100% rename from pylint/test/functional/none_dunder_protocols_py36.txt rename to tests/functional/none_dunder_protocols_py36.txt diff --git a/pylint/test/functional/nonexistent_operator.py b/tests/functional/nonexistent_operator.py similarity index 100% rename from pylint/test/functional/nonexistent_operator.py rename to tests/functional/nonexistent_operator.py diff --git a/pylint/test/functional/nonexistent_operator.txt b/tests/functional/nonexistent_operator.txt similarity index 100% rename from pylint/test/functional/nonexistent_operator.txt rename to tests/functional/nonexistent_operator.txt diff --git a/pylint/test/functional/nonlocal_and_global.py b/tests/functional/nonlocal_and_global.py similarity index 100% rename from pylint/test/functional/nonlocal_and_global.py rename to tests/functional/nonlocal_and_global.py diff --git a/pylint/test/functional/nonlocal_and_global.rc b/tests/functional/nonlocal_and_global.rc similarity index 100% rename from pylint/test/functional/nonlocal_and_global.rc rename to tests/functional/nonlocal_and_global.rc diff --git a/pylint/test/functional/nonlocal_and_global.txt b/tests/functional/nonlocal_and_global.txt similarity index 100% rename from pylint/test/functional/nonlocal_and_global.txt rename to tests/functional/nonlocal_and_global.txt diff --git a/pylint/test/functional/nonlocal_without_binding.py b/tests/functional/nonlocal_without_binding.py similarity index 100% rename from pylint/test/functional/nonlocal_without_binding.py rename to tests/functional/nonlocal_without_binding.py diff --git a/pylint/test/functional/nonlocal_without_binding.rc b/tests/functional/nonlocal_without_binding.rc similarity index 100% rename from pylint/test/functional/nonlocal_without_binding.rc rename to tests/functional/nonlocal_without_binding.rc diff --git a/pylint/test/functional/nonlocal_without_binding.txt b/tests/functional/nonlocal_without_binding.txt similarity index 100% rename from pylint/test/functional/nonlocal_without_binding.txt rename to tests/functional/nonlocal_without_binding.txt diff --git a/pylint/test/functional/not_async_context_manager.py b/tests/functional/not_async_context_manager.py similarity index 100% rename from pylint/test/functional/not_async_context_manager.py rename to tests/functional/not_async_context_manager.py diff --git a/pylint/test/functional/not_async_context_manager.rc b/tests/functional/not_async_context_manager.rc similarity index 100% rename from pylint/test/functional/not_async_context_manager.rc rename to tests/functional/not_async_context_manager.rc diff --git a/pylint/test/functional/not_async_context_manager.txt b/tests/functional/not_async_context_manager.txt similarity index 100% rename from pylint/test/functional/not_async_context_manager.txt rename to tests/functional/not_async_context_manager.txt diff --git a/pylint/test/functional/not_async_context_manager_py37.py b/tests/functional/not_async_context_manager_py37.py similarity index 100% rename from pylint/test/functional/not_async_context_manager_py37.py rename to tests/functional/not_async_context_manager_py37.py diff --git a/pylint/test/functional/not_async_context_manager_py37.rc b/tests/functional/not_async_context_manager_py37.rc similarity index 100% rename from pylint/test/functional/not_async_context_manager_py37.rc rename to tests/functional/not_async_context_manager_py37.rc diff --git a/pylint/test/functional/not_async_context_manager_py37.txt b/tests/functional/not_async_context_manager_py37.txt similarity index 100% rename from pylint/test/functional/not_async_context_manager_py37.txt rename to tests/functional/not_async_context_manager_py37.txt diff --git a/pylint/test/functional/not_callable.py b/tests/functional/not_callable.py similarity index 100% rename from pylint/test/functional/not_callable.py rename to tests/functional/not_callable.py diff --git a/pylint/test/functional/not_callable.txt b/tests/functional/not_callable.txt similarity index 100% rename from pylint/test/functional/not_callable.txt rename to tests/functional/not_callable.txt diff --git a/pylint/test/functional/not_context_manager.py b/tests/functional/not_context_manager.py similarity index 100% rename from pylint/test/functional/not_context_manager.py rename to tests/functional/not_context_manager.py diff --git a/pylint/test/functional/not_context_manager.txt b/tests/functional/not_context_manager.txt similarity index 100% rename from pylint/test/functional/not_context_manager.txt rename to tests/functional/not_context_manager.txt diff --git a/pylint/test/functional/not_in_loop.py b/tests/functional/not_in_loop.py similarity index 100% rename from pylint/test/functional/not_in_loop.py rename to tests/functional/not_in_loop.py diff --git a/pylint/test/functional/not_in_loop.txt b/tests/functional/not_in_loop.txt similarity index 100% rename from pylint/test/functional/not_in_loop.txt rename to tests/functional/not_in_loop.txt diff --git a/pylint/test/functional/old_division_manually.py b/tests/functional/old_division_manually.py similarity index 100% rename from pylint/test/functional/old_division_manually.py rename to tests/functional/old_division_manually.py diff --git a/pylint/test/functional/old_division_manually.rc b/tests/functional/old_division_manually.rc similarity index 100% rename from pylint/test/functional/old_division_manually.rc rename to tests/functional/old_division_manually.rc diff --git a/pylint/test/functional/postponed_evaluation_activated.py b/tests/functional/postponed_evaluation_activated.py similarity index 100% rename from pylint/test/functional/postponed_evaluation_activated.py rename to tests/functional/postponed_evaluation_activated.py diff --git a/pylint/test/functional/postponed_evaluation_activated.rc b/tests/functional/postponed_evaluation_activated.rc similarity index 100% rename from pylint/test/functional/postponed_evaluation_activated.rc rename to tests/functional/postponed_evaluation_activated.rc diff --git a/pylint/test/functional/postponed_evaluation_activated.txt b/tests/functional/postponed_evaluation_activated.txt similarity index 100% rename from pylint/test/functional/postponed_evaluation_activated.txt rename to tests/functional/postponed_evaluation_activated.txt diff --git a/pylint/test/functional/postponed_evaluation_not_activated.py b/tests/functional/postponed_evaluation_not_activated.py similarity index 100% rename from pylint/test/functional/postponed_evaluation_not_activated.py rename to tests/functional/postponed_evaluation_not_activated.py diff --git a/pylint/test/functional/postponed_evaluation_not_activated.rc b/tests/functional/postponed_evaluation_not_activated.rc similarity index 100% rename from pylint/test/functional/postponed_evaluation_not_activated.rc rename to tests/functional/postponed_evaluation_not_activated.rc diff --git a/pylint/test/functional/postponed_evaluation_not_activated.txt b/tests/functional/postponed_evaluation_not_activated.txt similarity index 100% rename from pylint/test/functional/postponed_evaluation_not_activated.txt rename to tests/functional/postponed_evaluation_not_activated.txt diff --git a/pylint/test/functional/pragma_after_backslash.py b/tests/functional/pragma_after_backslash.py similarity index 100% rename from pylint/test/functional/pragma_after_backslash.py rename to tests/functional/pragma_after_backslash.py diff --git a/pylint/test/functional/preferred_module.py b/tests/functional/preferred_module.py similarity index 100% rename from pylint/test/functional/preferred_module.py rename to tests/functional/preferred_module.py diff --git a/pylint/test/functional/preferred_module.rc b/tests/functional/preferred_module.rc similarity index 100% rename from pylint/test/functional/preferred_module.rc rename to tests/functional/preferred_module.rc diff --git a/pylint/test/functional/preferred_module.txt b/tests/functional/preferred_module.txt similarity index 100% rename from pylint/test/functional/preferred_module.txt rename to tests/functional/preferred_module.txt diff --git a/pylint/test/functional/print_always_warns.py b/tests/functional/print_always_warns.py similarity index 100% rename from pylint/test/functional/print_always_warns.py rename to tests/functional/print_always_warns.py diff --git a/pylint/test/functional/print_always_warns.rc b/tests/functional/print_always_warns.rc similarity index 100% rename from pylint/test/functional/print_always_warns.rc rename to tests/functional/print_always_warns.rc diff --git a/pylint/test/functional/print_always_warns.txt b/tests/functional/print_always_warns.txt similarity index 100% rename from pylint/test/functional/print_always_warns.txt rename to tests/functional/print_always_warns.txt diff --git a/pylint/test/functional/protected_access_access_different_scopes.py b/tests/functional/protected_access_access_different_scopes.py similarity index 100% rename from pylint/test/functional/protected_access_access_different_scopes.py rename to tests/functional/protected_access_access_different_scopes.py diff --git a/pylint/test/functional/protected_access_access_different_scopes.rc b/tests/functional/protected_access_access_different_scopes.rc similarity index 100% rename from pylint/test/functional/protected_access_access_different_scopes.rc rename to tests/functional/protected_access_access_different_scopes.rc diff --git a/pylint/test/functional/protected_access_access_different_scopes.txt b/tests/functional/protected_access_access_different_scopes.txt similarity index 100% rename from pylint/test/functional/protected_access_access_different_scopes.txt rename to tests/functional/protected_access_access_different_scopes.txt diff --git a/pylint/test/functional/raising_format_tuple.py b/tests/functional/raising_format_tuple.py similarity index 100% rename from pylint/test/functional/raising_format_tuple.py rename to tests/functional/raising_format_tuple.py diff --git a/pylint/test/functional/raising_format_tuple.txt b/tests/functional/raising_format_tuple.txt similarity index 100% rename from pylint/test/functional/raising_format_tuple.txt rename to tests/functional/raising_format_tuple.txt diff --git a/pylint/test/functional/raising_non_exception_py3.py b/tests/functional/raising_non_exception_py3.py similarity index 100% rename from pylint/test/functional/raising_non_exception_py3.py rename to tests/functional/raising_non_exception_py3.py diff --git a/pylint/test/functional/raising_non_exception_py3.rc b/tests/functional/raising_non_exception_py3.rc similarity index 100% rename from pylint/test/functional/raising_non_exception_py3.rc rename to tests/functional/raising_non_exception_py3.rc diff --git a/pylint/test/functional/raising_non_exception_py3.txt b/tests/functional/raising_non_exception_py3.txt similarity index 100% rename from pylint/test/functional/raising_non_exception_py3.txt rename to tests/functional/raising_non_exception_py3.txt diff --git a/pylint/test/functional/raising_self.py b/tests/functional/raising_self.py similarity index 100% rename from pylint/test/functional/raising_self.py rename to tests/functional/raising_self.py diff --git a/pylint/test/functional/raising_self.txt b/tests/functional/raising_self.txt similarity index 100% rename from pylint/test/functional/raising_self.txt rename to tests/functional/raising_self.txt diff --git a/pylint/test/functional/recursion_error_2667.py b/tests/functional/recursion_error_2667.py similarity index 100% rename from pylint/test/functional/recursion_error_2667.py rename to tests/functional/recursion_error_2667.py diff --git a/pylint/test/functional/recursion_error_2667.txt b/tests/functional/recursion_error_2667.txt similarity index 100% rename from pylint/test/functional/recursion_error_2667.txt rename to tests/functional/recursion_error_2667.txt diff --git a/pylint/test/functional/recursion_error_2906.py b/tests/functional/recursion_error_2906.py similarity index 100% rename from pylint/test/functional/recursion_error_2906.py rename to tests/functional/recursion_error_2906.py diff --git a/pylint/test/functional/recursion_error_940.py b/tests/functional/recursion_error_940.py similarity index 100% rename from pylint/test/functional/recursion_error_940.py rename to tests/functional/recursion_error_940.py diff --git a/pylint/test/functional/recursion_error_crash.py b/tests/functional/recursion_error_crash.py similarity index 100% rename from pylint/test/functional/recursion_error_crash.py rename to tests/functional/recursion_error_crash.py diff --git a/pylint/test/functional/recursion_error_crash.txt b/tests/functional/recursion_error_crash.txt similarity index 100% rename from pylint/test/functional/recursion_error_crash.txt rename to tests/functional/recursion_error_crash.txt diff --git a/pylint/test/functional/recursion_error_crash_2683.py b/tests/functional/recursion_error_crash_2683.py similarity index 100% rename from pylint/test/functional/recursion_error_crash_2683.py rename to tests/functional/recursion_error_crash_2683.py diff --git a/pylint/test/functional/recursion_error_crash_2683.txt b/tests/functional/recursion_error_crash_2683.txt similarity index 100% rename from pylint/test/functional/recursion_error_crash_2683.txt rename to tests/functional/recursion_error_crash_2683.txt diff --git a/pylint/test/functional/recursion_error_crash_astroid_623.py b/tests/functional/recursion_error_crash_astroid_623.py similarity index 100% rename from pylint/test/functional/recursion_error_crash_astroid_623.py rename to tests/functional/recursion_error_crash_astroid_623.py diff --git a/pylint/test/functional/recursion_error_crash_astroid_623.txt b/tests/functional/recursion_error_crash_astroid_623.txt similarity index 100% rename from pylint/test/functional/recursion_error_crash_astroid_623.txt rename to tests/functional/recursion_error_crash_astroid_623.txt diff --git a/pylint/test/functional/redefine_in_handler.py b/tests/functional/redefine_in_handler.py similarity index 100% rename from pylint/test/functional/redefine_in_handler.py rename to tests/functional/redefine_in_handler.py diff --git a/pylint/test/functional/redefine_in_handler.rc b/tests/functional/redefine_in_handler.rc similarity index 100% rename from pylint/test/functional/redefine_in_handler.rc rename to tests/functional/redefine_in_handler.rc diff --git a/pylint/test/functional/redefine_in_handler.txt b/tests/functional/redefine_in_handler.txt similarity index 100% rename from pylint/test/functional/redefine_in_handler.txt rename to tests/functional/redefine_in_handler.txt diff --git a/pylint/test/functional/redefined_argument_from_local.py b/tests/functional/redefined_argument_from_local.py similarity index 100% rename from pylint/test/functional/redefined_argument_from_local.py rename to tests/functional/redefined_argument_from_local.py diff --git a/pylint/test/functional/redefined_argument_from_local.txt b/tests/functional/redefined_argument_from_local.txt similarity index 100% rename from pylint/test/functional/redefined_argument_from_local.txt rename to tests/functional/redefined_argument_from_local.txt diff --git a/pylint/test/functional/redefined_builtin.py b/tests/functional/redefined_builtin.py similarity index 100% rename from pylint/test/functional/redefined_builtin.py rename to tests/functional/redefined_builtin.py diff --git a/pylint/test/functional/redefined_builtin.txt b/tests/functional/redefined_builtin.txt similarity index 100% rename from pylint/test/functional/redefined_builtin.txt rename to tests/functional/redefined_builtin.txt diff --git a/pylint/test/functional/redundant_unittest_assert.py b/tests/functional/redundant_unittest_assert.py similarity index 100% rename from pylint/test/functional/redundant_unittest_assert.py rename to tests/functional/redundant_unittest_assert.py diff --git a/pylint/test/functional/redundant_unittest_assert.txt b/tests/functional/redundant_unittest_assert.txt similarity index 100% rename from pylint/test/functional/redundant_unittest_assert.txt rename to tests/functional/redundant_unittest_assert.txt diff --git a/pylint/test/functional/regression_1326_crash_uninferable.py b/tests/functional/regression_1326_crash_uninferable.py similarity index 100% rename from pylint/test/functional/regression_1326_crash_uninferable.py rename to tests/functional/regression_1326_crash_uninferable.py diff --git a/pylint/test/functional/regression_2443_duplicate_bases.py b/tests/functional/regression_2443_duplicate_bases.py similarity index 100% rename from pylint/test/functional/regression_2443_duplicate_bases.py rename to tests/functional/regression_2443_duplicate_bases.py diff --git a/pylint/test/functional/regression_2443_duplicate_bases.rc b/tests/functional/regression_2443_duplicate_bases.rc similarity index 100% rename from pylint/test/functional/regression_2443_duplicate_bases.rc rename to tests/functional/regression_2443_duplicate_bases.rc diff --git a/pylint/test/functional/regression_2937_ifexp.py b/tests/functional/regression_2937_ifexp.py similarity index 100% rename from pylint/test/functional/regression_2937_ifexp.py rename to tests/functional/regression_2937_ifexp.py diff --git a/pylint/test/functional/regression_no_value_for_parameter.py b/tests/functional/regression_no_value_for_parameter.py similarity index 100% rename from pylint/test/functional/regression_no_value_for_parameter.py rename to tests/functional/regression_no_value_for_parameter.py diff --git a/pylint/test/functional/regression_no_value_for_parameter.txt b/tests/functional/regression_no_value_for_parameter.txt similarity index 100% rename from pylint/test/functional/regression_no_value_for_parameter.txt rename to tests/functional/regression_no_value_for_parameter.txt diff --git a/pylint/test/functional/reimported.py b/tests/functional/reimported.py similarity index 100% rename from pylint/test/functional/reimported.py rename to tests/functional/reimported.py diff --git a/pylint/test/functional/reimported.txt b/tests/functional/reimported.txt similarity index 100% rename from pylint/test/functional/reimported.txt rename to tests/functional/reimported.txt diff --git a/pylint/test/functional/repeated_keyword.py b/tests/functional/repeated_keyword.py similarity index 100% rename from pylint/test/functional/repeated_keyword.py rename to tests/functional/repeated_keyword.py diff --git a/pylint/test/functional/repeated_keyword.txt b/tests/functional/repeated_keyword.txt similarity index 100% rename from pylint/test/functional/repeated_keyword.txt rename to tests/functional/repeated_keyword.txt diff --git a/pylint/test/functional/return_in_init.py b/tests/functional/return_in_init.py similarity index 100% rename from pylint/test/functional/return_in_init.py rename to tests/functional/return_in_init.py diff --git a/pylint/test/functional/return_in_init.txt b/tests/functional/return_in_init.txt similarity index 100% rename from pylint/test/functional/return_in_init.txt rename to tests/functional/return_in_init.txt diff --git a/pylint/test/functional/return_outside_function.py b/tests/functional/return_outside_function.py similarity index 100% rename from pylint/test/functional/return_outside_function.py rename to tests/functional/return_outside_function.py diff --git a/pylint/test/functional/return_outside_function.txt b/tests/functional/return_outside_function.txt similarity index 100% rename from pylint/test/functional/return_outside_function.txt rename to tests/functional/return_outside_function.txt diff --git a/pylint/test/functional/reused_outer_loop_variable.py b/tests/functional/reused_outer_loop_variable.py similarity index 100% rename from pylint/test/functional/reused_outer_loop_variable.py rename to tests/functional/reused_outer_loop_variable.py diff --git a/pylint/test/functional/reused_outer_loop_variable.txt b/tests/functional/reused_outer_loop_variable.txt similarity index 100% rename from pylint/test/functional/reused_outer_loop_variable.txt rename to tests/functional/reused_outer_loop_variable.txt diff --git a/pylint/test/functional/reused_outer_loop_variable_py3.py b/tests/functional/reused_outer_loop_variable_py3.py similarity index 100% rename from pylint/test/functional/reused_outer_loop_variable_py3.py rename to tests/functional/reused_outer_loop_variable_py3.py diff --git a/pylint/test/functional/reused_outer_loop_variable_py3.rc b/tests/functional/reused_outer_loop_variable_py3.rc similarity index 100% rename from pylint/test/functional/reused_outer_loop_variable_py3.rc rename to tests/functional/reused_outer_loop_variable_py3.rc diff --git a/pylint/test/functional/reused_outer_loop_variable_py3.txt b/tests/functional/reused_outer_loop_variable_py3.txt similarity index 100% rename from pylint/test/functional/reused_outer_loop_variable_py3.txt rename to tests/functional/reused_outer_loop_variable_py3.txt diff --git a/pylint/test/functional/self_cls_assignment.py b/tests/functional/self_cls_assignment.py similarity index 100% rename from pylint/test/functional/self_cls_assignment.py rename to tests/functional/self_cls_assignment.py diff --git a/pylint/test/functional/self_cls_assignment.txt b/tests/functional/self_cls_assignment.txt similarity index 100% rename from pylint/test/functional/self_cls_assignment.txt rename to tests/functional/self_cls_assignment.txt diff --git a/pylint/test/functional/signature_differs.py b/tests/functional/signature_differs.py similarity index 100% rename from pylint/test/functional/signature_differs.py rename to tests/functional/signature_differs.py diff --git a/pylint/test/functional/signature_differs.txt b/tests/functional/signature_differs.txt similarity index 100% rename from pylint/test/functional/signature_differs.txt rename to tests/functional/signature_differs.txt diff --git a/pylint/test/functional/simplifiable_if_expression.py b/tests/functional/simplifiable_if_expression.py similarity index 100% rename from pylint/test/functional/simplifiable_if_expression.py rename to tests/functional/simplifiable_if_expression.py diff --git a/pylint/test/functional/simplifiable_if_expression.txt b/tests/functional/simplifiable_if_expression.txt similarity index 100% rename from pylint/test/functional/simplifiable_if_expression.txt rename to tests/functional/simplifiable_if_expression.txt diff --git a/pylint/test/functional/simplifiable_if_statement.py b/tests/functional/simplifiable_if_statement.py similarity index 100% rename from pylint/test/functional/simplifiable_if_statement.py rename to tests/functional/simplifiable_if_statement.py diff --git a/pylint/test/functional/simplifiable_if_statement.txt b/tests/functional/simplifiable_if_statement.txt similarity index 100% rename from pylint/test/functional/simplifiable_if_statement.txt rename to tests/functional/simplifiable_if_statement.txt diff --git a/pylint/test/functional/simplify_chained_comparison.py b/tests/functional/simplify_chained_comparison.py similarity index 100% rename from pylint/test/functional/simplify_chained_comparison.py rename to tests/functional/simplify_chained_comparison.py diff --git a/pylint/test/functional/simplify_chained_comparison.txt b/tests/functional/simplify_chained_comparison.txt similarity index 100% rename from pylint/test/functional/simplify_chained_comparison.txt rename to tests/functional/simplify_chained_comparison.txt diff --git a/pylint/test/functional/singledispatch_functions.py b/tests/functional/singledispatch_functions.py similarity index 100% rename from pylint/test/functional/singledispatch_functions.py rename to tests/functional/singledispatch_functions.py diff --git a/pylint/test/functional/singledispatch_functions.rc b/tests/functional/singledispatch_functions.rc similarity index 100% rename from pylint/test/functional/singledispatch_functions.rc rename to tests/functional/singledispatch_functions.rc diff --git a/pylint/test/functional/singledispatch_functions.txt b/tests/functional/singledispatch_functions.txt similarity index 100% rename from pylint/test/functional/singledispatch_functions.txt rename to tests/functional/singledispatch_functions.txt diff --git a/pylint/test/functional/singledispatch_functions_py3.py b/tests/functional/singledispatch_functions_py3.py similarity index 100% rename from pylint/test/functional/singledispatch_functions_py3.py rename to tests/functional/singledispatch_functions_py3.py diff --git a/pylint/test/functional/singledispatch_functions_py3.rc b/tests/functional/singledispatch_functions_py3.rc similarity index 100% rename from pylint/test/functional/singledispatch_functions_py3.rc rename to tests/functional/singledispatch_functions_py3.rc diff --git a/pylint/test/functional/singledispatch_functions_py3.txt b/tests/functional/singledispatch_functions_py3.txt similarity index 100% rename from pylint/test/functional/singledispatch_functions_py3.txt rename to tests/functional/singledispatch_functions_py3.txt diff --git a/pylint/test/functional/singleton_comparison.py b/tests/functional/singleton_comparison.py similarity index 100% rename from pylint/test/functional/singleton_comparison.py rename to tests/functional/singleton_comparison.py diff --git a/pylint/test/functional/singleton_comparison.txt b/tests/functional/singleton_comparison.txt similarity index 100% rename from pylint/test/functional/singleton_comparison.txt rename to tests/functional/singleton_comparison.txt diff --git a/pylint/test/functional/slots_checks.py b/tests/functional/slots_checks.py similarity index 100% rename from pylint/test/functional/slots_checks.py rename to tests/functional/slots_checks.py diff --git a/pylint/test/functional/slots_checks.txt b/tests/functional/slots_checks.txt similarity index 100% rename from pylint/test/functional/slots_checks.txt rename to tests/functional/slots_checks.txt diff --git a/pylint/test/functional/socketerror_import.py b/tests/functional/socketerror_import.py similarity index 100% rename from pylint/test/functional/socketerror_import.py rename to tests/functional/socketerror_import.py diff --git a/pylint/test/functional/star_needs_assignment_target.py b/tests/functional/star_needs_assignment_target.py similarity index 100% rename from pylint/test/functional/star_needs_assignment_target.py rename to tests/functional/star_needs_assignment_target.py diff --git a/pylint/test/functional/star_needs_assignment_target.rc b/tests/functional/star_needs_assignment_target.rc similarity index 100% rename from pylint/test/functional/star_needs_assignment_target.rc rename to tests/functional/star_needs_assignment_target.rc diff --git a/pylint/test/functional/star_needs_assignment_target.txt b/tests/functional/star_needs_assignment_target.txt similarity index 100% rename from pylint/test/functional/star_needs_assignment_target.txt rename to tests/functional/star_needs_assignment_target.txt diff --git a/pylint/test/functional/star_needs_assignment_target_py35.py b/tests/functional/star_needs_assignment_target_py35.py similarity index 100% rename from pylint/test/functional/star_needs_assignment_target_py35.py rename to tests/functional/star_needs_assignment_target_py35.py diff --git a/pylint/test/functional/star_needs_assignment_target_py35.rc b/tests/functional/star_needs_assignment_target_py35.rc similarity index 100% rename from pylint/test/functional/star_needs_assignment_target_py35.rc rename to tests/functional/star_needs_assignment_target_py35.rc diff --git a/pylint/test/functional/star_needs_assignment_target_py35.txt b/tests/functional/star_needs_assignment_target_py35.txt similarity index 100% rename from pylint/test/functional/star_needs_assignment_target_py35.txt rename to tests/functional/star_needs_assignment_target_py35.txt diff --git a/pylint/test/functional/statement_without_effect.py b/tests/functional/statement_without_effect.py similarity index 100% rename from pylint/test/functional/statement_without_effect.py rename to tests/functional/statement_without_effect.py diff --git a/pylint/test/functional/statement_without_effect.txt b/tests/functional/statement_without_effect.txt similarity index 100% rename from pylint/test/functional/statement_without_effect.txt rename to tests/functional/statement_without_effect.txt diff --git a/pylint/test/functional/statement_without_effect_py36.py b/tests/functional/statement_without_effect_py36.py similarity index 100% rename from pylint/test/functional/statement_without_effect_py36.py rename to tests/functional/statement_without_effect_py36.py diff --git a/pylint/test/functional/statement_without_effect_py36.rc b/tests/functional/statement_without_effect_py36.rc similarity index 100% rename from pylint/test/functional/statement_without_effect_py36.rc rename to tests/functional/statement_without_effect_py36.rc diff --git a/pylint/test/functional/statement_without_effect_py36.txt b/tests/functional/statement_without_effect_py36.txt similarity index 100% rename from pylint/test/functional/statement_without_effect_py36.txt rename to tests/functional/statement_without_effect_py36.txt diff --git a/pylint/test/functional/stop_iteration_inside_generator.py b/tests/functional/stop_iteration_inside_generator.py similarity index 100% rename from pylint/test/functional/stop_iteration_inside_generator.py rename to tests/functional/stop_iteration_inside_generator.py diff --git a/pylint/test/functional/stop_iteration_inside_generator.rc b/tests/functional/stop_iteration_inside_generator.rc similarity index 100% rename from pylint/test/functional/stop_iteration_inside_generator.rc rename to tests/functional/stop_iteration_inside_generator.rc diff --git a/pylint/test/functional/stop_iteration_inside_generator.txt b/tests/functional/stop_iteration_inside_generator.txt similarity index 100% rename from pylint/test/functional/stop_iteration_inside_generator.txt rename to tests/functional/stop_iteration_inside_generator.txt diff --git a/pylint/test/functional/string_formatting.py b/tests/functional/string_formatting.py similarity index 100% rename from pylint/test/functional/string_formatting.py rename to tests/functional/string_formatting.py diff --git a/pylint/test/functional/string_formatting.txt b/tests/functional/string_formatting.txt similarity index 100% rename from pylint/test/functional/string_formatting.txt rename to tests/functional/string_formatting.txt diff --git a/pylint/test/functional/string_formatting_disable.py b/tests/functional/string_formatting_disable.py similarity index 100% rename from pylint/test/functional/string_formatting_disable.py rename to tests/functional/string_formatting_disable.py diff --git a/pylint/test/functional/string_formatting_disable.rc b/tests/functional/string_formatting_disable.rc similarity index 100% rename from pylint/test/functional/string_formatting_disable.rc rename to tests/functional/string_formatting_disable.rc diff --git a/pylint/test/functional/string_formatting_disable.txt b/tests/functional/string_formatting_disable.txt similarity index 100% rename from pylint/test/functional/string_formatting_disable.txt rename to tests/functional/string_formatting_disable.txt diff --git a/pylint/test/functional/string_formatting_failed_inference.py b/tests/functional/string_formatting_failed_inference.py similarity index 100% rename from pylint/test/functional/string_formatting_failed_inference.py rename to tests/functional/string_formatting_failed_inference.py diff --git a/pylint/test/functional/string_formatting_failed_inference_py35.py b/tests/functional/string_formatting_failed_inference_py35.py similarity index 100% rename from pylint/test/functional/string_formatting_failed_inference_py35.py rename to tests/functional/string_formatting_failed_inference_py35.py diff --git a/pylint/test/functional/string_formatting_failed_inference_py35.rc b/tests/functional/string_formatting_failed_inference_py35.rc similarity index 100% rename from pylint/test/functional/string_formatting_failed_inference_py35.rc rename to tests/functional/string_formatting_failed_inference_py35.rc diff --git a/pylint/test/functional/string_formatting_py27.py b/tests/functional/string_formatting_py27.py similarity index 100% rename from pylint/test/functional/string_formatting_py27.py rename to tests/functional/string_formatting_py27.py diff --git a/pylint/test/functional/string_formatting_py27.rc b/tests/functional/string_formatting_py27.rc similarity index 100% rename from pylint/test/functional/string_formatting_py27.rc rename to tests/functional/string_formatting_py27.rc diff --git a/pylint/test/functional/string_formatting_py27.txt b/tests/functional/string_formatting_py27.txt similarity index 100% rename from pylint/test/functional/string_formatting_py27.txt rename to tests/functional/string_formatting_py27.txt diff --git a/pylint/test/functional/string_formatting_py3.py b/tests/functional/string_formatting_py3.py similarity index 100% rename from pylint/test/functional/string_formatting_py3.py rename to tests/functional/string_formatting_py3.py diff --git a/pylint/test/functional/string_formatting_py3.rc b/tests/functional/string_formatting_py3.rc similarity index 100% rename from pylint/test/functional/string_formatting_py3.rc rename to tests/functional/string_formatting_py3.rc diff --git a/pylint/test/functional/string_formatting_py3.txt b/tests/functional/string_formatting_py3.txt similarity index 100% rename from pylint/test/functional/string_formatting_py3.txt rename to tests/functional/string_formatting_py3.txt diff --git a/pylint/test/functional/subprocess_popen_preexec_fn.py b/tests/functional/subprocess_popen_preexec_fn.py similarity index 100% rename from pylint/test/functional/subprocess_popen_preexec_fn.py rename to tests/functional/subprocess_popen_preexec_fn.py diff --git a/pylint/test/functional/subprocess_popen_preexec_fn.txt b/tests/functional/subprocess_popen_preexec_fn.txt similarity index 100% rename from pylint/test/functional/subprocess_popen_preexec_fn.txt rename to tests/functional/subprocess_popen_preexec_fn.txt diff --git a/pylint/test/functional/subprocess_run_check35.py b/tests/functional/subprocess_run_check35.py similarity index 100% rename from pylint/test/functional/subprocess_run_check35.py rename to tests/functional/subprocess_run_check35.py diff --git a/pylint/test/functional/subprocess_run_check35.rc b/tests/functional/subprocess_run_check35.rc similarity index 100% rename from pylint/test/functional/subprocess_run_check35.rc rename to tests/functional/subprocess_run_check35.rc diff --git a/pylint/test/functional/subprocess_run_check35.txt b/tests/functional/subprocess_run_check35.txt similarity index 100% rename from pylint/test/functional/subprocess_run_check35.txt rename to tests/functional/subprocess_run_check35.txt diff --git a/pylint/test/functional/super_checks.py b/tests/functional/super_checks.py similarity index 100% rename from pylint/test/functional/super_checks.py rename to tests/functional/super_checks.py diff --git a/pylint/test/functional/super_checks.txt b/tests/functional/super_checks.txt similarity index 100% rename from pylint/test/functional/super_checks.txt rename to tests/functional/super_checks.txt diff --git a/pylint/test/functional/superfluous_parens.py b/tests/functional/superfluous_parens.py similarity index 100% rename from pylint/test/functional/superfluous_parens.py rename to tests/functional/superfluous_parens.py diff --git a/pylint/test/functional/superfluous_parens.txt b/tests/functional/superfluous_parens.txt similarity index 100% rename from pylint/test/functional/superfluous_parens.txt rename to tests/functional/superfluous_parens.txt diff --git a/pylint/test/functional/suspicious_str_strip_call.py b/tests/functional/suspicious_str_strip_call.py similarity index 100% rename from pylint/test/functional/suspicious_str_strip_call.py rename to tests/functional/suspicious_str_strip_call.py diff --git a/pylint/test/functional/suspicious_str_strip_call.rc b/tests/functional/suspicious_str_strip_call.rc similarity index 100% rename from pylint/test/functional/suspicious_str_strip_call.rc rename to tests/functional/suspicious_str_strip_call.rc diff --git a/pylint/test/functional/suspicious_str_strip_call.txt b/tests/functional/suspicious_str_strip_call.txt similarity index 100% rename from pylint/test/functional/suspicious_str_strip_call.txt rename to tests/functional/suspicious_str_strip_call.txt diff --git a/pylint/test/functional/suspicious_str_strip_call_py3.py b/tests/functional/suspicious_str_strip_call_py3.py similarity index 100% rename from pylint/test/functional/suspicious_str_strip_call_py3.py rename to tests/functional/suspicious_str_strip_call_py3.py diff --git a/pylint/test/functional/suspicious_str_strip_call_py3.rc b/tests/functional/suspicious_str_strip_call_py3.rc similarity index 100% rename from pylint/test/functional/suspicious_str_strip_call_py3.rc rename to tests/functional/suspicious_str_strip_call_py3.rc diff --git a/pylint/test/functional/suspicious_str_strip_call_py3.txt b/tests/functional/suspicious_str_strip_call_py3.txt similarity index 100% rename from pylint/test/functional/suspicious_str_strip_call_py3.txt rename to tests/functional/suspicious_str_strip_call_py3.txt diff --git a/pylint/test/functional/syntax_error.py b/tests/functional/syntax_error.py similarity index 100% rename from pylint/test/functional/syntax_error.py rename to tests/functional/syntax_error.py diff --git a/pylint/test/functional/syntax_error.rc b/tests/functional/syntax_error.rc similarity index 100% rename from pylint/test/functional/syntax_error.rc rename to tests/functional/syntax_error.rc diff --git a/pylint/test/functional/syntax_error.txt b/tests/functional/syntax_error.txt similarity index 100% rename from pylint/test/functional/syntax_error.txt rename to tests/functional/syntax_error.txt diff --git a/pylint/test/functional/syntax_error_jython.py b/tests/functional/syntax_error_jython.py similarity index 100% rename from pylint/test/functional/syntax_error_jython.py rename to tests/functional/syntax_error_jython.py diff --git a/pylint/test/functional/syntax_error_jython.rc b/tests/functional/syntax_error_jython.rc similarity index 100% rename from pylint/test/functional/syntax_error_jython.rc rename to tests/functional/syntax_error_jython.rc diff --git a/pylint/test/functional/syntax_error_jython.txt b/tests/functional/syntax_error_jython.txt similarity index 100% rename from pylint/test/functional/syntax_error_jython.txt rename to tests/functional/syntax_error_jython.txt diff --git a/pylint/test/functional/sys_stream_regression_1004.py b/tests/functional/sys_stream_regression_1004.py similarity index 100% rename from pylint/test/functional/sys_stream_regression_1004.py rename to tests/functional/sys_stream_regression_1004.py diff --git a/pylint/test/functional/sys_stream_regression_1004.rc b/tests/functional/sys_stream_regression_1004.rc similarity index 100% rename from pylint/test/functional/sys_stream_regression_1004.rc rename to tests/functional/sys_stream_regression_1004.rc diff --git a/pylint/test/functional/sys_stream_regression_1004.txt b/tests/functional/sys_stream_regression_1004.txt similarity index 100% rename from pylint/test/functional/sys_stream_regression_1004.txt rename to tests/functional/sys_stream_regression_1004.txt diff --git a/pylint/test/functional/ternary.py b/tests/functional/ternary.py similarity index 100% rename from pylint/test/functional/ternary.py rename to tests/functional/ternary.py diff --git a/pylint/test/functional/ternary.txt b/tests/functional/ternary.txt similarity index 100% rename from pylint/test/functional/ternary.txt rename to tests/functional/ternary.txt diff --git a/pylint/test/functional/test_compile.py b/tests/functional/test_compile.py similarity index 100% rename from pylint/test/functional/test_compile.py rename to tests/functional/test_compile.py diff --git a/pylint/test/functional/tokenize_error.py b/tests/functional/tokenize_error.py similarity index 100% rename from pylint/test/functional/tokenize_error.py rename to tests/functional/tokenize_error.py diff --git a/pylint/test/functional/tokenize_error.rc b/tests/functional/tokenize_error.rc similarity index 100% rename from pylint/test/functional/tokenize_error.rc rename to tests/functional/tokenize_error.rc diff --git a/pylint/test/functional/tokenize_error.txt b/tests/functional/tokenize_error.txt similarity index 100% rename from pylint/test/functional/tokenize_error.txt rename to tests/functional/tokenize_error.txt diff --git a/pylint/test/functional/tokenize_error_jython.py b/tests/functional/tokenize_error_jython.py similarity index 100% rename from pylint/test/functional/tokenize_error_jython.py rename to tests/functional/tokenize_error_jython.py diff --git a/pylint/test/functional/tokenize_error_jython.rc b/tests/functional/tokenize_error_jython.rc similarity index 100% rename from pylint/test/functional/tokenize_error_jython.rc rename to tests/functional/tokenize_error_jython.rc diff --git a/pylint/test/functional/tokenize_error_jython.txt b/tests/functional/tokenize_error_jython.txt similarity index 100% rename from pylint/test/functional/tokenize_error_jython.txt rename to tests/functional/tokenize_error_jython.txt diff --git a/pylint/test/functional/too_few_public_methods.py b/tests/functional/too_few_public_methods.py similarity index 100% rename from pylint/test/functional/too_few_public_methods.py rename to tests/functional/too_few_public_methods.py diff --git a/pylint/test/functional/too_few_public_methods.txt b/tests/functional/too_few_public_methods.txt similarity index 100% rename from pylint/test/functional/too_few_public_methods.txt rename to tests/functional/too_few_public_methods.txt diff --git a/pylint/test/functional/too_few_public_methods_37.py b/tests/functional/too_few_public_methods_37.py similarity index 100% rename from pylint/test/functional/too_few_public_methods_37.py rename to tests/functional/too_few_public_methods_37.py diff --git a/pylint/test/functional/too_few_public_methods_37.rc b/tests/functional/too_few_public_methods_37.rc similarity index 100% rename from pylint/test/functional/too_few_public_methods_37.rc rename to tests/functional/too_few_public_methods_37.rc diff --git a/pylint/test/functional/too_few_public_methods_37.txt b/tests/functional/too_few_public_methods_37.txt similarity index 100% rename from pylint/test/functional/too_few_public_methods_37.txt rename to tests/functional/too_few_public_methods_37.txt diff --git a/pylint/test/functional/too_many_ancestors.py b/tests/functional/too_many_ancestors.py similarity index 100% rename from pylint/test/functional/too_many_ancestors.py rename to tests/functional/too_many_ancestors.py diff --git a/pylint/test/functional/too_many_ancestors.txt b/tests/functional/too_many_ancestors.txt similarity index 100% rename from pylint/test/functional/too_many_ancestors.txt rename to tests/functional/too_many_ancestors.txt diff --git a/pylint/test/functional/too_many_arguments.py b/tests/functional/too_many_arguments.py similarity index 100% rename from pylint/test/functional/too_many_arguments.py rename to tests/functional/too_many_arguments.py diff --git a/pylint/test/functional/too_many_arguments.txt b/tests/functional/too_many_arguments.txt similarity index 100% rename from pylint/test/functional/too_many_arguments.txt rename to tests/functional/too_many_arguments.txt diff --git a/pylint/test/functional/too_many_arguments_issue_1045.py b/tests/functional/too_many_arguments_issue_1045.py similarity index 100% rename from pylint/test/functional/too_many_arguments_issue_1045.py rename to tests/functional/too_many_arguments_issue_1045.py diff --git a/pylint/test/functional/too_many_arguments_issue_1045.rc b/tests/functional/too_many_arguments_issue_1045.rc similarity index 100% rename from pylint/test/functional/too_many_arguments_issue_1045.rc rename to tests/functional/too_many_arguments_issue_1045.rc diff --git a/pylint/test/functional/too_many_arguments_issue_1045.txt b/tests/functional/too_many_arguments_issue_1045.txt similarity index 100% rename from pylint/test/functional/too_many_arguments_issue_1045.txt rename to tests/functional/too_many_arguments_issue_1045.txt diff --git a/pylint/test/functional/too_many_boolean_expressions.py b/tests/functional/too_many_boolean_expressions.py similarity index 100% rename from pylint/test/functional/too_many_boolean_expressions.py rename to tests/functional/too_many_boolean_expressions.py diff --git a/pylint/test/functional/too_many_boolean_expressions.txt b/tests/functional/too_many_boolean_expressions.txt similarity index 100% rename from pylint/test/functional/too_many_boolean_expressions.txt rename to tests/functional/too_many_boolean_expressions.txt diff --git a/pylint/test/functional/too_many_branches.py b/tests/functional/too_many_branches.py similarity index 100% rename from pylint/test/functional/too_many_branches.py rename to tests/functional/too_many_branches.py diff --git a/pylint/test/functional/too_many_branches.txt b/tests/functional/too_many_branches.txt similarity index 100% rename from pylint/test/functional/too_many_branches.txt rename to tests/functional/too_many_branches.txt diff --git a/pylint/test/functional/too_many_instance_attributes.py b/tests/functional/too_many_instance_attributes.py similarity index 100% rename from pylint/test/functional/too_many_instance_attributes.py rename to tests/functional/too_many_instance_attributes.py diff --git a/pylint/test/functional/too_many_instance_attributes.txt b/tests/functional/too_many_instance_attributes.txt similarity index 100% rename from pylint/test/functional/too_many_instance_attributes.txt rename to tests/functional/too_many_instance_attributes.txt diff --git a/pylint/test/functional/too_many_lines.py b/tests/functional/too_many_lines.py similarity index 100% rename from pylint/test/functional/too_many_lines.py rename to tests/functional/too_many_lines.py diff --git a/pylint/test/functional/too_many_lines.txt b/tests/functional/too_many_lines.txt similarity index 100% rename from pylint/test/functional/too_many_lines.txt rename to tests/functional/too_many_lines.txt diff --git a/pylint/test/functional/too_many_lines_disabled.py b/tests/functional/too_many_lines_disabled.py similarity index 100% rename from pylint/test/functional/too_many_lines_disabled.py rename to tests/functional/too_many_lines_disabled.py diff --git a/pylint/test/functional/too_many_locals.py b/tests/functional/too_many_locals.py similarity index 100% rename from pylint/test/functional/too_many_locals.py rename to tests/functional/too_many_locals.py diff --git a/pylint/test/functional/too_many_locals.txt b/tests/functional/too_many_locals.txt similarity index 100% rename from pylint/test/functional/too_many_locals.txt rename to tests/functional/too_many_locals.txt diff --git a/pylint/test/functional/too_many_nested_blocks.py b/tests/functional/too_many_nested_blocks.py similarity index 100% rename from pylint/test/functional/too_many_nested_blocks.py rename to tests/functional/too_many_nested_blocks.py diff --git a/pylint/test/functional/too_many_nested_blocks.txt b/tests/functional/too_many_nested_blocks.txt similarity index 100% rename from pylint/test/functional/too_many_nested_blocks.txt rename to tests/functional/too_many_nested_blocks.txt diff --git a/pylint/test/functional/too_many_public_methods.py b/tests/functional/too_many_public_methods.py similarity index 100% rename from pylint/test/functional/too_many_public_methods.py rename to tests/functional/too_many_public_methods.py diff --git a/pylint/test/functional/too_many_public_methods.txt b/tests/functional/too_many_public_methods.txt similarity index 100% rename from pylint/test/functional/too_many_public_methods.txt rename to tests/functional/too_many_public_methods.txt diff --git a/pylint/test/functional/too_many_return_statements.py b/tests/functional/too_many_return_statements.py similarity index 100% rename from pylint/test/functional/too_many_return_statements.py rename to tests/functional/too_many_return_statements.py diff --git a/pylint/test/functional/too_many_return_statements.txt b/tests/functional/too_many_return_statements.txt similarity index 100% rename from pylint/test/functional/too_many_return_statements.txt rename to tests/functional/too_many_return_statements.txt diff --git a/pylint/test/functional/too_many_star_expressions.py b/tests/functional/too_many_star_expressions.py similarity index 100% rename from pylint/test/functional/too_many_star_expressions.py rename to tests/functional/too_many_star_expressions.py diff --git a/pylint/test/functional/too_many_star_expressions.rc b/tests/functional/too_many_star_expressions.rc similarity index 100% rename from pylint/test/functional/too_many_star_expressions.rc rename to tests/functional/too_many_star_expressions.rc diff --git a/pylint/test/functional/too_many_star_expressions.txt b/tests/functional/too_many_star_expressions.txt similarity index 100% rename from pylint/test/functional/too_many_star_expressions.txt rename to tests/functional/too_many_star_expressions.txt diff --git a/pylint/test/functional/too_many_statements.py b/tests/functional/too_many_statements.py similarity index 100% rename from pylint/test/functional/too_many_statements.py rename to tests/functional/too_many_statements.py diff --git a/pylint/test/functional/too_many_statements.txt b/tests/functional/too_many_statements.txt similarity index 100% rename from pylint/test/functional/too_many_statements.txt rename to tests/functional/too_many_statements.txt diff --git a/pylint/test/functional/trailing_comma_tuple.py b/tests/functional/trailing_comma_tuple.py similarity index 100% rename from pylint/test/functional/trailing_comma_tuple.py rename to tests/functional/trailing_comma_tuple.py diff --git a/pylint/test/functional/trailing_comma_tuple.rc b/tests/functional/trailing_comma_tuple.rc similarity index 100% rename from pylint/test/functional/trailing_comma_tuple.rc rename to tests/functional/trailing_comma_tuple.rc diff --git a/pylint/test/functional/trailing_comma_tuple.txt b/tests/functional/trailing_comma_tuple.txt similarity index 100% rename from pylint/test/functional/trailing_comma_tuple.txt rename to tests/functional/trailing_comma_tuple.txt diff --git a/pylint/test/functional/trailing_newlines.py b/tests/functional/trailing_newlines.py similarity index 100% rename from pylint/test/functional/trailing_newlines.py rename to tests/functional/trailing_newlines.py diff --git a/pylint/test/functional/trailing_newlines.txt b/tests/functional/trailing_newlines.txt similarity index 100% rename from pylint/test/functional/trailing_newlines.txt rename to tests/functional/trailing_newlines.txt diff --git a/pylint/test/functional/trailing_whitespaces.py b/tests/functional/trailing_whitespaces.py similarity index 100% rename from pylint/test/functional/trailing_whitespaces.py rename to tests/functional/trailing_whitespaces.py diff --git a/pylint/test/functional/trailing_whitespaces.txt b/tests/functional/trailing_whitespaces.txt similarity index 100% rename from pylint/test/functional/trailing_whitespaces.txt rename to tests/functional/trailing_whitespaces.txt diff --git a/pylint/test/functional/try_except_raise.py b/tests/functional/try_except_raise.py similarity index 100% rename from pylint/test/functional/try_except_raise.py rename to tests/functional/try_except_raise.py diff --git a/pylint/test/functional/try_except_raise.txt b/tests/functional/try_except_raise.txt similarity index 100% rename from pylint/test/functional/try_except_raise.txt rename to tests/functional/try_except_raise.txt diff --git a/pylint/test/functional/try_except_raise_crash.py b/tests/functional/try_except_raise_crash.py similarity index 100% rename from pylint/test/functional/try_except_raise_crash.py rename to tests/functional/try_except_raise_crash.py diff --git a/pylint/test/functional/try_except_raise_crash.txt b/tests/functional/try_except_raise_crash.txt similarity index 100% rename from pylint/test/functional/try_except_raise_crash.txt rename to tests/functional/try_except_raise_crash.txt diff --git a/pylint/test/functional/typing_use.py b/tests/functional/typing_use.py similarity index 100% rename from pylint/test/functional/typing_use.py rename to tests/functional/typing_use.py diff --git a/pylint/test/functional/typing_use.rc b/tests/functional/typing_use.rc similarity index 100% rename from pylint/test/functional/typing_use.rc rename to tests/functional/typing_use.rc diff --git a/pylint/test/functional/typing_use.txt b/tests/functional/typing_use.txt similarity index 100% rename from pylint/test/functional/typing_use.txt rename to tests/functional/typing_use.txt diff --git a/pylint/test/functional/unbalanced_tuple_unpacking.py b/tests/functional/unbalanced_tuple_unpacking.py similarity index 100% rename from pylint/test/functional/unbalanced_tuple_unpacking.py rename to tests/functional/unbalanced_tuple_unpacking.py diff --git a/pylint/test/functional/unbalanced_tuple_unpacking.txt b/tests/functional/unbalanced_tuple_unpacking.txt similarity index 100% rename from pylint/test/functional/unbalanced_tuple_unpacking.txt rename to tests/functional/unbalanced_tuple_unpacking.txt diff --git a/pylint/test/functional/unbalanced_tuple_unpacking_py30.py b/tests/functional/unbalanced_tuple_unpacking_py30.py similarity index 100% rename from pylint/test/functional/unbalanced_tuple_unpacking_py30.py rename to tests/functional/unbalanced_tuple_unpacking_py30.py diff --git a/pylint/test/functional/unbalanced_tuple_unpacking_py30.rc b/tests/functional/unbalanced_tuple_unpacking_py30.rc similarity index 100% rename from pylint/test/functional/unbalanced_tuple_unpacking_py30.rc rename to tests/functional/unbalanced_tuple_unpacking_py30.rc diff --git a/pylint/test/functional/undefined_loop_variable.py b/tests/functional/undefined_loop_variable.py similarity index 100% rename from pylint/test/functional/undefined_loop_variable.py rename to tests/functional/undefined_loop_variable.py diff --git a/pylint/test/functional/undefined_loop_variable.txt b/tests/functional/undefined_loop_variable.txt similarity index 100% rename from pylint/test/functional/undefined_loop_variable.txt rename to tests/functional/undefined_loop_variable.txt diff --git a/pylint/test/functional/undefined_variable.py b/tests/functional/undefined_variable.py similarity index 100% rename from pylint/test/functional/undefined_variable.py rename to tests/functional/undefined_variable.py diff --git a/pylint/test/functional/undefined_variable.txt b/tests/functional/undefined_variable.txt similarity index 100% rename from pylint/test/functional/undefined_variable.txt rename to tests/functional/undefined_variable.txt diff --git a/pylint/test/functional/undefined_variable_py30.py b/tests/functional/undefined_variable_py30.py similarity index 100% rename from pylint/test/functional/undefined_variable_py30.py rename to tests/functional/undefined_variable_py30.py diff --git a/pylint/test/functional/undefined_variable_py30.rc b/tests/functional/undefined_variable_py30.rc similarity index 100% rename from pylint/test/functional/undefined_variable_py30.rc rename to tests/functional/undefined_variable_py30.rc diff --git a/pylint/test/functional/undefined_variable_py30.txt b/tests/functional/undefined_variable_py30.txt similarity index 100% rename from pylint/test/functional/undefined_variable_py30.txt rename to tests/functional/undefined_variable_py30.txt diff --git a/pylint/test/functional/unexpected_special_method_signature.py b/tests/functional/unexpected_special_method_signature.py similarity index 100% rename from pylint/test/functional/unexpected_special_method_signature.py rename to tests/functional/unexpected_special_method_signature.py diff --git a/pylint/test/functional/unexpected_special_method_signature.txt b/tests/functional/unexpected_special_method_signature.txt similarity index 100% rename from pylint/test/functional/unexpected_special_method_signature.txt rename to tests/functional/unexpected_special_method_signature.txt diff --git a/pylint/test/functional/ungrouped_imports.py b/tests/functional/ungrouped_imports.py similarity index 100% rename from pylint/test/functional/ungrouped_imports.py rename to tests/functional/ungrouped_imports.py diff --git a/pylint/test/functional/ungrouped_imports.txt b/tests/functional/ungrouped_imports.txt similarity index 100% rename from pylint/test/functional/ungrouped_imports.txt rename to tests/functional/ungrouped_imports.txt diff --git a/pylint/test/functional/ungrouped_imports_isort_compatible.py b/tests/functional/ungrouped_imports_isort_compatible.py similarity index 100% rename from pylint/test/functional/ungrouped_imports_isort_compatible.py rename to tests/functional/ungrouped_imports_isort_compatible.py diff --git a/pylint/test/functional/ungrouped_imports_isort_compatible.txt b/tests/functional/ungrouped_imports_isort_compatible.txt similarity index 100% rename from pylint/test/functional/ungrouped_imports_isort_compatible.txt rename to tests/functional/ungrouped_imports_isort_compatible.txt diff --git a/pylint/test/functional/unhashable_dict_key.py b/tests/functional/unhashable_dict_key.py similarity index 100% rename from pylint/test/functional/unhashable_dict_key.py rename to tests/functional/unhashable_dict_key.py diff --git a/pylint/test/functional/unhashable_dict_key.txt b/tests/functional/unhashable_dict_key.txt similarity index 100% rename from pylint/test/functional/unhashable_dict_key.txt rename to tests/functional/unhashable_dict_key.txt diff --git a/pylint/test/functional/unidiomatic_typecheck.py b/tests/functional/unidiomatic_typecheck.py similarity index 100% rename from pylint/test/functional/unidiomatic_typecheck.py rename to tests/functional/unidiomatic_typecheck.py diff --git a/pylint/test/functional/unidiomatic_typecheck.txt b/tests/functional/unidiomatic_typecheck.txt similarity index 100% rename from pylint/test/functional/unidiomatic_typecheck.txt rename to tests/functional/unidiomatic_typecheck.txt diff --git a/pylint/test/functional/uninferable_all_object.py b/tests/functional/uninferable_all_object.py similarity index 100% rename from pylint/test/functional/uninferable_all_object.py rename to tests/functional/uninferable_all_object.py diff --git a/pylint/test/functional/unknown_encoding_jython.py b/tests/functional/unknown_encoding_jython.py similarity index 100% rename from pylint/test/functional/unknown_encoding_jython.py rename to tests/functional/unknown_encoding_jython.py diff --git a/pylint/test/functional/unknown_encoding_jython.rc b/tests/functional/unknown_encoding_jython.rc similarity index 100% rename from pylint/test/functional/unknown_encoding_jython.rc rename to tests/functional/unknown_encoding_jython.rc diff --git a/pylint/test/functional/unknown_encoding_jython.txt b/tests/functional/unknown_encoding_jython.txt similarity index 100% rename from pylint/test/functional/unknown_encoding_jython.txt rename to tests/functional/unknown_encoding_jython.txt diff --git a/pylint/test/functional/unknown_encoding_py29.py b/tests/functional/unknown_encoding_py29.py similarity index 100% rename from pylint/test/functional/unknown_encoding_py29.py rename to tests/functional/unknown_encoding_py29.py diff --git a/pylint/test/functional/unknown_encoding_py29.rc b/tests/functional/unknown_encoding_py29.rc similarity index 100% rename from pylint/test/functional/unknown_encoding_py29.rc rename to tests/functional/unknown_encoding_py29.rc diff --git a/pylint/test/functional/unknown_encoding_py29.txt b/tests/functional/unknown_encoding_py29.txt similarity index 100% rename from pylint/test/functional/unknown_encoding_py29.txt rename to tests/functional/unknown_encoding_py29.txt diff --git a/pylint/test/functional/unknown_encoding_pypy.py b/tests/functional/unknown_encoding_pypy.py similarity index 100% rename from pylint/test/functional/unknown_encoding_pypy.py rename to tests/functional/unknown_encoding_pypy.py diff --git a/pylint/test/functional/unknown_encoding_pypy.rc b/tests/functional/unknown_encoding_pypy.rc similarity index 100% rename from pylint/test/functional/unknown_encoding_pypy.rc rename to tests/functional/unknown_encoding_pypy.rc diff --git a/pylint/test/functional/unknown_encoding_pypy.txt b/tests/functional/unknown_encoding_pypy.txt similarity index 100% rename from pylint/test/functional/unknown_encoding_pypy.txt rename to tests/functional/unknown_encoding_pypy.txt diff --git a/pylint/test/functional/unnecessary_lambda.py b/tests/functional/unnecessary_lambda.py similarity index 100% rename from pylint/test/functional/unnecessary_lambda.py rename to tests/functional/unnecessary_lambda.py diff --git a/pylint/test/functional/unnecessary_lambda.txt b/tests/functional/unnecessary_lambda.txt similarity index 100% rename from pylint/test/functional/unnecessary_lambda.txt rename to tests/functional/unnecessary_lambda.txt diff --git a/pylint/test/functional/unnecessary_pass.py b/tests/functional/unnecessary_pass.py similarity index 100% rename from pylint/test/functional/unnecessary_pass.py rename to tests/functional/unnecessary_pass.py diff --git a/pylint/test/functional/unnecessary_pass.txt b/tests/functional/unnecessary_pass.txt similarity index 100% rename from pylint/test/functional/unnecessary_pass.txt rename to tests/functional/unnecessary_pass.txt diff --git a/pylint/test/functional/unneeded_not.py b/tests/functional/unneeded_not.py similarity index 100% rename from pylint/test/functional/unneeded_not.py rename to tests/functional/unneeded_not.py diff --git a/pylint/test/functional/unneeded_not.txt b/tests/functional/unneeded_not.txt similarity index 100% rename from pylint/test/functional/unneeded_not.txt rename to tests/functional/unneeded_not.txt diff --git a/pylint/test/functional/unpacked_exceptions.py b/tests/functional/unpacked_exceptions.py similarity index 100% rename from pylint/test/functional/unpacked_exceptions.py rename to tests/functional/unpacked_exceptions.py diff --git a/pylint/test/functional/unpacked_exceptions.rc b/tests/functional/unpacked_exceptions.rc similarity index 100% rename from pylint/test/functional/unpacked_exceptions.rc rename to tests/functional/unpacked_exceptions.rc diff --git a/pylint/test/functional/unpacked_exceptions.txt b/tests/functional/unpacked_exceptions.txt similarity index 100% rename from pylint/test/functional/unpacked_exceptions.txt rename to tests/functional/unpacked_exceptions.txt diff --git a/pylint/test/functional/unpacking.py b/tests/functional/unpacking.py similarity index 100% rename from pylint/test/functional/unpacking.py rename to tests/functional/unpacking.py diff --git a/pylint/test/functional/unpacking_generalizations.py b/tests/functional/unpacking_generalizations.py similarity index 100% rename from pylint/test/functional/unpacking_generalizations.py rename to tests/functional/unpacking_generalizations.py diff --git a/pylint/test/functional/unpacking_generalizations.rc b/tests/functional/unpacking_generalizations.rc similarity index 100% rename from pylint/test/functional/unpacking_generalizations.rc rename to tests/functional/unpacking_generalizations.rc diff --git a/pylint/test/functional/unpacking_generalizations.txt b/tests/functional/unpacking_generalizations.txt similarity index 100% rename from pylint/test/functional/unpacking_generalizations.txt rename to tests/functional/unpacking_generalizations.txt diff --git a/pylint/test/functional/unpacking_non_sequence.py b/tests/functional/unpacking_non_sequence.py similarity index 100% rename from pylint/test/functional/unpacking_non_sequence.py rename to tests/functional/unpacking_non_sequence.py diff --git a/pylint/test/functional/unpacking_non_sequence.txt b/tests/functional/unpacking_non_sequence.txt similarity index 100% rename from pylint/test/functional/unpacking_non_sequence.txt rename to tests/functional/unpacking_non_sequence.txt diff --git a/pylint/test/functional/unreachable.py b/tests/functional/unreachable.py similarity index 100% rename from pylint/test/functional/unreachable.py rename to tests/functional/unreachable.py diff --git a/pylint/test/functional/unreachable.txt b/tests/functional/unreachable.txt similarity index 100% rename from pylint/test/functional/unreachable.txt rename to tests/functional/unreachable.txt diff --git a/pylint/test/functional/unrecognized_inline_option.py b/tests/functional/unrecognized_inline_option.py similarity index 100% rename from pylint/test/functional/unrecognized_inline_option.py rename to tests/functional/unrecognized_inline_option.py diff --git a/pylint/test/functional/unrecognized_inline_option.txt b/tests/functional/unrecognized_inline_option.txt similarity index 100% rename from pylint/test/functional/unrecognized_inline_option.txt rename to tests/functional/unrecognized_inline_option.txt diff --git a/pylint/test/functional/unsubscriptable_value.py b/tests/functional/unsubscriptable_value.py similarity index 100% rename from pylint/test/functional/unsubscriptable_value.py rename to tests/functional/unsubscriptable_value.py diff --git a/pylint/test/functional/unsubscriptable_value.txt b/tests/functional/unsubscriptable_value.txt similarity index 100% rename from pylint/test/functional/unsubscriptable_value.txt rename to tests/functional/unsubscriptable_value.txt diff --git a/pylint/test/functional/unsubscriptable_value_py37.py b/tests/functional/unsubscriptable_value_py37.py similarity index 100% rename from pylint/test/functional/unsubscriptable_value_py37.py rename to tests/functional/unsubscriptable_value_py37.py diff --git a/pylint/test/functional/unsubscriptable_value_py37.rc b/tests/functional/unsubscriptable_value_py37.rc similarity index 100% rename from pylint/test/functional/unsubscriptable_value_py37.rc rename to tests/functional/unsubscriptable_value_py37.rc diff --git a/pylint/test/functional/unsubscriptable_value_py37.txt b/tests/functional/unsubscriptable_value_py37.txt similarity index 100% rename from pylint/test/functional/unsubscriptable_value_py37.txt rename to tests/functional/unsubscriptable_value_py37.txt diff --git a/pylint/test/functional/unsupported_assignment_operation.py b/tests/functional/unsupported_assignment_operation.py similarity index 100% rename from pylint/test/functional/unsupported_assignment_operation.py rename to tests/functional/unsupported_assignment_operation.py diff --git a/pylint/test/functional/unsupported_assignment_operation.txt b/tests/functional/unsupported_assignment_operation.txt similarity index 100% rename from pylint/test/functional/unsupported_assignment_operation.txt rename to tests/functional/unsupported_assignment_operation.txt diff --git a/pylint/test/functional/unsupported_binary_operation.py b/tests/functional/unsupported_binary_operation.py similarity index 100% rename from pylint/test/functional/unsupported_binary_operation.py rename to tests/functional/unsupported_binary_operation.py diff --git a/pylint/test/functional/unsupported_binary_operation.rc b/tests/functional/unsupported_binary_operation.rc similarity index 100% rename from pylint/test/functional/unsupported_binary_operation.rc rename to tests/functional/unsupported_binary_operation.rc diff --git a/pylint/test/functional/unsupported_binary_operation.txt b/tests/functional/unsupported_binary_operation.txt similarity index 100% rename from pylint/test/functional/unsupported_binary_operation.txt rename to tests/functional/unsupported_binary_operation.txt diff --git a/pylint/test/functional/unsupported_delete_operation.py b/tests/functional/unsupported_delete_operation.py similarity index 100% rename from pylint/test/functional/unsupported_delete_operation.py rename to tests/functional/unsupported_delete_operation.py diff --git a/pylint/test/functional/unsupported_delete_operation.txt b/tests/functional/unsupported_delete_operation.txt similarity index 100% rename from pylint/test/functional/unsupported_delete_operation.txt rename to tests/functional/unsupported_delete_operation.txt diff --git a/pylint/test/functional/unused_argument.py b/tests/functional/unused_argument.py similarity index 100% rename from pylint/test/functional/unused_argument.py rename to tests/functional/unused_argument.py diff --git a/pylint/test/functional/unused_argument.txt b/tests/functional/unused_argument.txt similarity index 100% rename from pylint/test/functional/unused_argument.txt rename to tests/functional/unused_argument.txt diff --git a/pylint/test/functional/unused_argument_py3.py b/tests/functional/unused_argument_py3.py similarity index 100% rename from pylint/test/functional/unused_argument_py3.py rename to tests/functional/unused_argument_py3.py diff --git a/pylint/test/functional/unused_argument_py3.rc b/tests/functional/unused_argument_py3.rc similarity index 100% rename from pylint/test/functional/unused_argument_py3.rc rename to tests/functional/unused_argument_py3.rc diff --git a/pylint/test/functional/unused_argument_py3.txt b/tests/functional/unused_argument_py3.txt similarity index 100% rename from pylint/test/functional/unused_argument_py3.txt rename to tests/functional/unused_argument_py3.txt diff --git a/pylint/test/functional/unused_global_variable1.py b/tests/functional/unused_global_variable1.py similarity index 100% rename from pylint/test/functional/unused_global_variable1.py rename to tests/functional/unused_global_variable1.py diff --git a/pylint/test/functional/unused_global_variable2.py b/tests/functional/unused_global_variable2.py similarity index 100% rename from pylint/test/functional/unused_global_variable2.py rename to tests/functional/unused_global_variable2.py diff --git a/pylint/test/functional/unused_global_variable2.rc b/tests/functional/unused_global_variable2.rc similarity index 100% rename from pylint/test/functional/unused_global_variable2.rc rename to tests/functional/unused_global_variable2.rc diff --git a/pylint/test/functional/unused_global_variable2.txt b/tests/functional/unused_global_variable2.txt similarity index 100% rename from pylint/test/functional/unused_global_variable2.txt rename to tests/functional/unused_global_variable2.txt diff --git a/pylint/test/functional/unused_global_variable3.py b/tests/functional/unused_global_variable3.py similarity index 100% rename from pylint/test/functional/unused_global_variable3.py rename to tests/functional/unused_global_variable3.py diff --git a/pylint/test/functional/unused_global_variable4.py b/tests/functional/unused_global_variable4.py similarity index 100% rename from pylint/test/functional/unused_global_variable4.py rename to tests/functional/unused_global_variable4.py diff --git a/pylint/test/functional/unused_global_variable4.rc b/tests/functional/unused_global_variable4.rc similarity index 100% rename from pylint/test/functional/unused_global_variable4.rc rename to tests/functional/unused_global_variable4.rc diff --git a/pylint/test/functional/unused_global_variable4.txt b/tests/functional/unused_global_variable4.txt similarity index 100% rename from pylint/test/functional/unused_global_variable4.txt rename to tests/functional/unused_global_variable4.txt diff --git a/pylint/test/functional/unused_import.py b/tests/functional/unused_import.py similarity index 100% rename from pylint/test/functional/unused_import.py rename to tests/functional/unused_import.py diff --git a/pylint/test/functional/unused_import.txt b/tests/functional/unused_import.txt similarity index 100% rename from pylint/test/functional/unused_import.txt rename to tests/functional/unused_import.txt diff --git a/pylint/test/functional/unused_import_assigned_to.py b/tests/functional/unused_import_assigned_to.py similarity index 100% rename from pylint/test/functional/unused_import_assigned_to.py rename to tests/functional/unused_import_assigned_to.py diff --git a/pylint/test/functional/unused_typing_imports.py b/tests/functional/unused_typing_imports.py similarity index 100% rename from pylint/test/functional/unused_typing_imports.py rename to tests/functional/unused_typing_imports.py diff --git a/pylint/test/functional/unused_typing_imports.rc b/tests/functional/unused_typing_imports.rc similarity index 100% rename from pylint/test/functional/unused_typing_imports.rc rename to tests/functional/unused_typing_imports.rc diff --git a/pylint/test/functional/unused_variable.py b/tests/functional/unused_variable.py similarity index 100% rename from pylint/test/functional/unused_variable.py rename to tests/functional/unused_variable.py diff --git a/pylint/test/functional/unused_variable.txt b/tests/functional/unused_variable.txt similarity index 100% rename from pylint/test/functional/unused_variable.txt rename to tests/functional/unused_variable.txt diff --git a/pylint/test/functional/unused_variable_py36.py b/tests/functional/unused_variable_py36.py similarity index 100% rename from pylint/test/functional/unused_variable_py36.py rename to tests/functional/unused_variable_py36.py diff --git a/pylint/test/functional/unused_variable_py36.rc b/tests/functional/unused_variable_py36.rc similarity index 100% rename from pylint/test/functional/unused_variable_py36.rc rename to tests/functional/unused_variable_py36.rc diff --git a/pylint/test/functional/unused_variable_py36.txt b/tests/functional/unused_variable_py36.txt similarity index 100% rename from pylint/test/functional/unused_variable_py36.txt rename to tests/functional/unused_variable_py36.txt diff --git a/pylint/test/functional/used_before_assignment_488.py b/tests/functional/used_before_assignment_488.py similarity index 100% rename from pylint/test/functional/used_before_assignment_488.py rename to tests/functional/used_before_assignment_488.py diff --git a/pylint/test/functional/used_before_assignment_issue1081.py b/tests/functional/used_before_assignment_issue1081.py similarity index 100% rename from pylint/test/functional/used_before_assignment_issue1081.py rename to tests/functional/used_before_assignment_issue1081.py diff --git a/pylint/test/functional/used_before_assignment_issue1081.txt b/tests/functional/used_before_assignment_issue1081.txt similarity index 100% rename from pylint/test/functional/used_before_assignment_issue1081.txt rename to tests/functional/used_before_assignment_issue1081.txt diff --git a/pylint/test/functional/used_before_assignment_issue853.py b/tests/functional/used_before_assignment_issue853.py similarity index 100% rename from pylint/test/functional/used_before_assignment_issue853.py rename to tests/functional/used_before_assignment_issue853.py diff --git a/pylint/test/functional/used_before_assignment_nonlocal.py b/tests/functional/used_before_assignment_nonlocal.py similarity index 100% rename from pylint/test/functional/used_before_assignment_nonlocal.py rename to tests/functional/used_before_assignment_nonlocal.py diff --git a/pylint/test/functional/used_before_assignment_nonlocal.rc b/tests/functional/used_before_assignment_nonlocal.rc similarity index 100% rename from pylint/test/functional/used_before_assignment_nonlocal.rc rename to tests/functional/used_before_assignment_nonlocal.rc diff --git a/pylint/test/functional/used_before_assignment_nonlocal.txt b/tests/functional/used_before_assignment_nonlocal.txt similarity index 100% rename from pylint/test/functional/used_before_assignment_nonlocal.txt rename to tests/functional/used_before_assignment_nonlocal.txt diff --git a/pylint/test/functional/used_prior_global_declaration.py b/tests/functional/used_prior_global_declaration.py similarity index 100% rename from pylint/test/functional/used_prior_global_declaration.py rename to tests/functional/used_prior_global_declaration.py diff --git a/pylint/test/functional/used_prior_global_declaration.rc b/tests/functional/used_prior_global_declaration.rc similarity index 100% rename from pylint/test/functional/used_prior_global_declaration.rc rename to tests/functional/used_prior_global_declaration.rc diff --git a/pylint/test/functional/used_prior_global_declaration.txt b/tests/functional/used_prior_global_declaration.txt similarity index 100% rename from pylint/test/functional/used_prior_global_declaration.txt rename to tests/functional/used_prior_global_declaration.txt diff --git a/pylint/test/functional/useless-import-alias.py b/tests/functional/useless-import-alias.py similarity index 100% rename from pylint/test/functional/useless-import-alias.py rename to tests/functional/useless-import-alias.py diff --git a/pylint/test/functional/useless-import-alias.txt b/tests/functional/useless-import-alias.txt similarity index 100% rename from pylint/test/functional/useless-import-alias.txt rename to tests/functional/useless-import-alias.txt diff --git a/pylint/test/functional/useless_else_on_loop.py b/tests/functional/useless_else_on_loop.py similarity index 100% rename from pylint/test/functional/useless_else_on_loop.py rename to tests/functional/useless_else_on_loop.py diff --git a/pylint/test/functional/useless_else_on_loop.txt b/tests/functional/useless_else_on_loop.txt similarity index 100% rename from pylint/test/functional/useless_else_on_loop.txt rename to tests/functional/useless_else_on_loop.txt diff --git a/pylint/test/functional/useless_object_inheritance.py b/tests/functional/useless_object_inheritance.py similarity index 100% rename from pylint/test/functional/useless_object_inheritance.py rename to tests/functional/useless_object_inheritance.py diff --git a/pylint/test/functional/useless_object_inheritance.txt b/tests/functional/useless_object_inheritance.txt similarity index 100% rename from pylint/test/functional/useless_object_inheritance.txt rename to tests/functional/useless_object_inheritance.txt diff --git a/pylint/test/functional/useless_return.py b/tests/functional/useless_return.py similarity index 100% rename from pylint/test/functional/useless_return.py rename to tests/functional/useless_return.py diff --git a/pylint/test/functional/useless_return.txt b/tests/functional/useless_return.txt similarity index 100% rename from pylint/test/functional/useless_return.txt rename to tests/functional/useless_return.txt diff --git a/pylint/test/functional/useless_super_delegation.py b/tests/functional/useless_super_delegation.py similarity index 100% rename from pylint/test/functional/useless_super_delegation.py rename to tests/functional/useless_super_delegation.py diff --git a/pylint/test/functional/useless_super_delegation.txt b/tests/functional/useless_super_delegation.txt similarity index 100% rename from pylint/test/functional/useless_super_delegation.txt rename to tests/functional/useless_super_delegation.txt diff --git a/pylint/test/functional/useless_super_delegation_py3.py b/tests/functional/useless_super_delegation_py3.py similarity index 100% rename from pylint/test/functional/useless_super_delegation_py3.py rename to tests/functional/useless_super_delegation_py3.py diff --git a/pylint/test/functional/useless_super_delegation_py3.rc b/tests/functional/useless_super_delegation_py3.rc similarity index 100% rename from pylint/test/functional/useless_super_delegation_py3.rc rename to tests/functional/useless_super_delegation_py3.rc diff --git a/pylint/test/functional/useless_super_delegation_py3.txt b/tests/functional/useless_super_delegation_py3.txt similarity index 100% rename from pylint/test/functional/useless_super_delegation_py3.txt rename to tests/functional/useless_super_delegation_py3.txt diff --git a/pylint/test/functional/useless_super_delegation_py35.py b/tests/functional/useless_super_delegation_py35.py similarity index 100% rename from pylint/test/functional/useless_super_delegation_py35.py rename to tests/functional/useless_super_delegation_py35.py diff --git a/pylint/test/functional/useless_super_delegation_py35.rc b/tests/functional/useless_super_delegation_py35.rc similarity index 100% rename from pylint/test/functional/useless_super_delegation_py35.rc rename to tests/functional/useless_super_delegation_py35.rc diff --git a/pylint/test/functional/useless_super_delegation_py35.txt b/tests/functional/useless_super_delegation_py35.txt similarity index 100% rename from pylint/test/functional/useless_super_delegation_py35.txt rename to tests/functional/useless_super_delegation_py35.txt diff --git a/pylint/test/functional/using_constant_test.py b/tests/functional/using_constant_test.py similarity index 100% rename from pylint/test/functional/using_constant_test.py rename to tests/functional/using_constant_test.py diff --git a/pylint/test/functional/using_constant_test.txt b/tests/functional/using_constant_test.txt similarity index 100% rename from pylint/test/functional/using_constant_test.txt rename to tests/functional/using_constant_test.txt diff --git a/pylint/test/functional/wildcard_import.py b/tests/functional/wildcard_import.py similarity index 100% rename from pylint/test/functional/wildcard_import.py rename to tests/functional/wildcard_import.py diff --git a/pylint/test/functional/wildcard_import.txt b/tests/functional/wildcard_import.txt similarity index 100% rename from pylint/test/functional/wildcard_import.txt rename to tests/functional/wildcard_import.txt diff --git a/pylint/test/functional/wildcard_import_allowed.py b/tests/functional/wildcard_import_allowed.py similarity index 100% rename from pylint/test/functional/wildcard_import_allowed.py rename to tests/functional/wildcard_import_allowed.py diff --git a/pylint/test/functional/wildcard_import_allowed.rc b/tests/functional/wildcard_import_allowed.rc similarity index 100% rename from pylint/test/functional/wildcard_import_allowed.rc rename to tests/functional/wildcard_import_allowed.rc diff --git a/pylint/test/functional/wildcard_import_allowed.txt b/tests/functional/wildcard_import_allowed.txt similarity index 100% rename from pylint/test/functional/wildcard_import_allowed.txt rename to tests/functional/wildcard_import_allowed.txt diff --git a/pylint/test/functional/with_used_before_assign.py b/tests/functional/with_used_before_assign.py similarity index 100% rename from pylint/test/functional/with_used_before_assign.py rename to tests/functional/with_used_before_assign.py diff --git a/pylint/test/functional/with_used_before_assign.txt b/tests/functional/with_used_before_assign.txt similarity index 100% rename from pylint/test/functional/with_used_before_assign.txt rename to tests/functional/with_used_before_assign.txt diff --git a/pylint/test/functional/with_using_generator.py b/tests/functional/with_using_generator.py similarity index 100% rename from pylint/test/functional/with_using_generator.py rename to tests/functional/with_using_generator.py diff --git a/pylint/test/functional/with_using_generator.txt b/tests/functional/with_using_generator.txt similarity index 100% rename from pylint/test/functional/with_using_generator.txt rename to tests/functional/with_using_generator.txt diff --git a/pylint/test/functional/wrong_exception_operation.py b/tests/functional/wrong_exception_operation.py similarity index 100% rename from pylint/test/functional/wrong_exception_operation.py rename to tests/functional/wrong_exception_operation.py diff --git a/pylint/test/functional/wrong_exception_operation.txt b/tests/functional/wrong_exception_operation.txt similarity index 100% rename from pylint/test/functional/wrong_exception_operation.txt rename to tests/functional/wrong_exception_operation.txt diff --git a/pylint/test/functional/wrong_import_order.py b/tests/functional/wrong_import_order.py similarity index 100% rename from pylint/test/functional/wrong_import_order.py rename to tests/functional/wrong_import_order.py diff --git a/pylint/test/functional/wrong_import_order.txt b/tests/functional/wrong_import_order.txt similarity index 100% rename from pylint/test/functional/wrong_import_order.txt rename to tests/functional/wrong_import_order.txt diff --git a/pylint/test/functional/wrong_import_order2.py b/tests/functional/wrong_import_order2.py similarity index 100% rename from pylint/test/functional/wrong_import_order2.py rename to tests/functional/wrong_import_order2.py diff --git a/pylint/test/functional/wrong_import_position.py b/tests/functional/wrong_import_position.py similarity index 100% rename from pylint/test/functional/wrong_import_position.py rename to tests/functional/wrong_import_position.py diff --git a/pylint/test/functional/wrong_import_position.txt b/tests/functional/wrong_import_position.txt similarity index 100% rename from pylint/test/functional/wrong_import_position.txt rename to tests/functional/wrong_import_position.txt diff --git a/pylint/test/functional/wrong_import_position10.py b/tests/functional/wrong_import_position10.py similarity index 100% rename from pylint/test/functional/wrong_import_position10.py rename to tests/functional/wrong_import_position10.py diff --git a/pylint/test/functional/wrong_import_position11.py b/tests/functional/wrong_import_position11.py similarity index 100% rename from pylint/test/functional/wrong_import_position11.py rename to tests/functional/wrong_import_position11.py diff --git a/pylint/test/functional/wrong_import_position11.txt b/tests/functional/wrong_import_position11.txt similarity index 100% rename from pylint/test/functional/wrong_import_position11.txt rename to tests/functional/wrong_import_position11.txt diff --git a/pylint/test/functional/wrong_import_position12.py b/tests/functional/wrong_import_position12.py similarity index 100% rename from pylint/test/functional/wrong_import_position12.py rename to tests/functional/wrong_import_position12.py diff --git a/pylint/test/functional/wrong_import_position12.txt b/tests/functional/wrong_import_position12.txt similarity index 100% rename from pylint/test/functional/wrong_import_position12.txt rename to tests/functional/wrong_import_position12.txt diff --git a/pylint/test/functional/wrong_import_position13.py b/tests/functional/wrong_import_position13.py similarity index 100% rename from pylint/test/functional/wrong_import_position13.py rename to tests/functional/wrong_import_position13.py diff --git a/pylint/test/functional/wrong_import_position13.txt b/tests/functional/wrong_import_position13.txt similarity index 100% rename from pylint/test/functional/wrong_import_position13.txt rename to tests/functional/wrong_import_position13.txt diff --git a/pylint/test/functional/wrong_import_position14.py b/tests/functional/wrong_import_position14.py similarity index 100% rename from pylint/test/functional/wrong_import_position14.py rename to tests/functional/wrong_import_position14.py diff --git a/pylint/test/functional/wrong_import_position14.txt b/tests/functional/wrong_import_position14.txt similarity index 100% rename from pylint/test/functional/wrong_import_position14.txt rename to tests/functional/wrong_import_position14.txt diff --git a/pylint/test/functional/wrong_import_position15.py b/tests/functional/wrong_import_position15.py similarity index 100% rename from pylint/test/functional/wrong_import_position15.py rename to tests/functional/wrong_import_position15.py diff --git a/pylint/test/functional/wrong_import_position2.py b/tests/functional/wrong_import_position2.py similarity index 100% rename from pylint/test/functional/wrong_import_position2.py rename to tests/functional/wrong_import_position2.py diff --git a/pylint/test/functional/wrong_import_position3.py b/tests/functional/wrong_import_position3.py similarity index 100% rename from pylint/test/functional/wrong_import_position3.py rename to tests/functional/wrong_import_position3.py diff --git a/pylint/test/functional/wrong_import_position4.py b/tests/functional/wrong_import_position4.py similarity index 100% rename from pylint/test/functional/wrong_import_position4.py rename to tests/functional/wrong_import_position4.py diff --git a/pylint/test/functional/wrong_import_position5.py b/tests/functional/wrong_import_position5.py similarity index 100% rename from pylint/test/functional/wrong_import_position5.py rename to tests/functional/wrong_import_position5.py diff --git a/pylint/test/functional/wrong_import_position6.py b/tests/functional/wrong_import_position6.py similarity index 100% rename from pylint/test/functional/wrong_import_position6.py rename to tests/functional/wrong_import_position6.py diff --git a/pylint/test/functional/wrong_import_position7.py b/tests/functional/wrong_import_position7.py similarity index 100% rename from pylint/test/functional/wrong_import_position7.py rename to tests/functional/wrong_import_position7.py diff --git a/pylint/test/functional/wrong_import_position8.py b/tests/functional/wrong_import_position8.py similarity index 100% rename from pylint/test/functional/wrong_import_position8.py rename to tests/functional/wrong_import_position8.py diff --git a/pylint/test/functional/wrong_import_position9.py b/tests/functional/wrong_import_position9.py similarity index 100% rename from pylint/test/functional/wrong_import_position9.py rename to tests/functional/wrong_import_position9.py diff --git a/pylint/test/functional/wrong_import_position_exclude_dunder_main.py b/tests/functional/wrong_import_position_exclude_dunder_main.py similarity index 100% rename from pylint/test/functional/wrong_import_position_exclude_dunder_main.py rename to tests/functional/wrong_import_position_exclude_dunder_main.py diff --git a/pylint/test/functional/wrong_import_position_exclude_dunder_main.txt b/tests/functional/wrong_import_position_exclude_dunder_main.txt similarity index 100% rename from pylint/test/functional/wrong_import_position_exclude_dunder_main.txt rename to tests/functional/wrong_import_position_exclude_dunder_main.txt diff --git a/pylint/test/functional/yield_from_iterable_py33.py b/tests/functional/yield_from_iterable_py33.py similarity index 100% rename from pylint/test/functional/yield_from_iterable_py33.py rename to tests/functional/yield_from_iterable_py33.py diff --git a/pylint/test/functional/yield_from_iterable_py33.rc b/tests/functional/yield_from_iterable_py33.rc similarity index 100% rename from pylint/test/functional/yield_from_iterable_py33.rc rename to tests/functional/yield_from_iterable_py33.rc diff --git a/pylint/test/functional/yield_from_iterable_py33.txt b/tests/functional/yield_from_iterable_py33.txt similarity index 100% rename from pylint/test/functional/yield_from_iterable_py33.txt rename to tests/functional/yield_from_iterable_py33.txt diff --git a/pylint/test/functional/yield_from_outside_func.py b/tests/functional/yield_from_outside_func.py similarity index 100% rename from pylint/test/functional/yield_from_outside_func.py rename to tests/functional/yield_from_outside_func.py diff --git a/pylint/test/functional/yield_from_outside_func.rc b/tests/functional/yield_from_outside_func.rc similarity index 100% rename from pylint/test/functional/yield_from_outside_func.rc rename to tests/functional/yield_from_outside_func.rc diff --git a/pylint/test/functional/yield_from_outside_func.txt b/tests/functional/yield_from_outside_func.txt similarity index 100% rename from pylint/test/functional/yield_from_outside_func.txt rename to tests/functional/yield_from_outside_func.txt diff --git a/pylint/test/functional/yield_inside_async_function.py b/tests/functional/yield_inside_async_function.py similarity index 100% rename from pylint/test/functional/yield_inside_async_function.py rename to tests/functional/yield_inside_async_function.py diff --git a/pylint/test/functional/yield_inside_async_function.rc b/tests/functional/yield_inside_async_function.rc similarity index 100% rename from pylint/test/functional/yield_inside_async_function.rc rename to tests/functional/yield_inside_async_function.rc diff --git a/pylint/test/functional/yield_inside_async_function.txt b/tests/functional/yield_inside_async_function.txt similarity index 100% rename from pylint/test/functional/yield_inside_async_function.txt rename to tests/functional/yield_inside_async_function.txt diff --git a/pylint/test/functional/yield_inside_async_function_py36.py b/tests/functional/yield_inside_async_function_py36.py similarity index 100% rename from pylint/test/functional/yield_inside_async_function_py36.py rename to tests/functional/yield_inside_async_function_py36.py diff --git a/pylint/test/functional/yield_inside_async_function_py36.rc b/tests/functional/yield_inside_async_function_py36.rc similarity index 100% rename from pylint/test/functional/yield_inside_async_function_py36.rc rename to tests/functional/yield_inside_async_function_py36.rc diff --git a/pylint/test/functional/yield_inside_async_function_py36.txt b/tests/functional/yield_inside_async_function_py36.txt similarity index 100% rename from pylint/test/functional/yield_inside_async_function_py36.txt rename to tests/functional/yield_inside_async_function_py36.txt diff --git a/pylint/test/functional/yield_outside_func.py b/tests/functional/yield_outside_func.py similarity index 100% rename from pylint/test/functional/yield_outside_func.py rename to tests/functional/yield_outside_func.py diff --git a/pylint/test/functional/yield_outside_func.txt b/tests/functional/yield_outside_func.txt similarity index 100% rename from pylint/test/functional/yield_outside_func.txt rename to tests/functional/yield_outside_func.txt diff --git a/pylint/test/input/__init__.py b/tests/input/__init__.py similarity index 100% rename from pylint/test/input/__init__.py rename to tests/input/__init__.py diff --git a/pylint/test/input/func_3k_removed_stuff_py_30.py b/tests/input/func_3k_removed_stuff_py_30.py similarity index 100% rename from pylint/test/input/func_3k_removed_stuff_py_30.py rename to tests/input/func_3k_removed_stuff_py_30.py diff --git a/pylint/test/input/func_bad_cont_dictcomp_py27.py b/tests/input/func_bad_cont_dictcomp_py27.py similarity index 100% rename from pylint/test/input/func_bad_cont_dictcomp_py27.py rename to tests/input/func_bad_cont_dictcomp_py27.py diff --git a/pylint/test/input/func_bug113231.py b/tests/input/func_bug113231.py similarity index 100% rename from pylint/test/input/func_bug113231.py rename to tests/input/func_bug113231.py diff --git a/pylint/test/input/func_disable_linebased.py b/tests/input/func_disable_linebased.py similarity index 100% rename from pylint/test/input/func_disable_linebased.py rename to tests/input/func_disable_linebased.py diff --git a/pylint/test/input/func_dotted_ancestor.py b/tests/input/func_dotted_ancestor.py similarity index 100% rename from pylint/test/input/func_dotted_ancestor.py rename to tests/input/func_dotted_ancestor.py diff --git a/pylint/test/input/func_e0012.py b/tests/input/func_e0012.py similarity index 100% rename from pylint/test/input/func_e0012.py rename to tests/input/func_e0012.py diff --git a/pylint/test/input/func_e0204.py b/tests/input/func_e0204.py similarity index 100% rename from pylint/test/input/func_e0204.py rename to tests/input/func_e0204.py diff --git a/pylint/test/input/func_e12xx.py b/tests/input/func_e12xx.py similarity index 100% rename from pylint/test/input/func_e12xx.py rename to tests/input/func_e12xx.py diff --git a/pylint/test/input/func_e13xx.py b/tests/input/func_e13xx.py similarity index 100% rename from pylint/test/input/func_e13xx.py rename to tests/input/func_e13xx.py diff --git a/pylint/test/input/func_excess_escapes.py b/tests/input/func_excess_escapes.py similarity index 100% rename from pylint/test/input/func_excess_escapes.py rename to tests/input/func_excess_escapes.py diff --git a/pylint/test/input/func_first_arg.py b/tests/input/func_first_arg.py similarity index 100% rename from pylint/test/input/func_first_arg.py rename to tests/input/func_first_arg.py diff --git a/pylint/test/input/func_i0011.py b/tests/input/func_i0011.py similarity index 100% rename from pylint/test/input/func_i0011.py rename to tests/input/func_i0011.py diff --git a/pylint/test/input/func_i0012.py b/tests/input/func_i0012.py similarity index 100% rename from pylint/test/input/func_i0012.py rename to tests/input/func_i0012.py diff --git a/pylint/test/input/func_i0013.py b/tests/input/func_i0013.py similarity index 100% rename from pylint/test/input/func_i0013.py rename to tests/input/func_i0013.py diff --git a/pylint/test/input/func_i0014.py b/tests/input/func_i0014.py similarity index 100% rename from pylint/test/input/func_i0014.py rename to tests/input/func_i0014.py diff --git a/pylint/test/input/func_i0020.py b/tests/input/func_i0020.py similarity index 100% rename from pylint/test/input/func_i0020.py rename to tests/input/func_i0020.py diff --git a/pylint/test/input/func_i0022.py b/tests/input/func_i0022.py similarity index 100% rename from pylint/test/input/func_i0022.py rename to tests/input/func_i0022.py diff --git a/pylint/test/input/func_logging_not_lazy_with_logger.py b/tests/input/func_logging_not_lazy_with_logger.py similarity index 100% rename from pylint/test/input/func_logging_not_lazy_with_logger.py rename to tests/input/func_logging_not_lazy_with_logger.py diff --git a/pylint/test/input/func_loopvar_in_dict_comp_py27.py b/tests/input/func_loopvar_in_dict_comp_py27.py similarity index 100% rename from pylint/test/input/func_loopvar_in_dict_comp_py27.py rename to tests/input/func_loopvar_in_dict_comp_py27.py diff --git a/pylint/test/input/func_module___dict__.py b/tests/input/func_module___dict__.py similarity index 100% rename from pylint/test/input/func_module___dict__.py rename to tests/input/func_module___dict__.py diff --git a/pylint/test/input/func_nameerror_on_string_substitution.py b/tests/input/func_nameerror_on_string_substitution.py similarity index 100% rename from pylint/test/input/func_nameerror_on_string_substitution.py rename to tests/input/func_nameerror_on_string_substitution.py diff --git a/pylint/test/input/func_no_dummy_redefined.py b/tests/input/func_no_dummy_redefined.py similarity index 100% rename from pylint/test/input/func_no_dummy_redefined.py rename to tests/input/func_no_dummy_redefined.py diff --git a/pylint/test/input/func_noerror___init___return_from_inner_function.py b/tests/input/func_noerror___init___return_from_inner_function.py similarity index 100% rename from pylint/test/input/func_noerror___init___return_from_inner_function.py rename to tests/input/func_noerror___init___return_from_inner_function.py diff --git a/pylint/test/input/func_noerror_access_attr_before_def_false_positive.py b/tests/input/func_noerror_access_attr_before_def_false_positive.py similarity index 100% rename from pylint/test/input/func_noerror_access_attr_before_def_false_positive.py rename to tests/input/func_noerror_access_attr_before_def_false_positive.py diff --git a/pylint/test/input/func_noerror_base_init_vars.py b/tests/input/func_noerror_base_init_vars.py similarity index 100% rename from pylint/test/input/func_noerror_base_init_vars.py rename to tests/input/func_noerror_base_init_vars.py diff --git a/pylint/test/input/func_noerror_builtin_module_test.py b/tests/input/func_noerror_builtin_module_test.py similarity index 100% rename from pylint/test/input/func_noerror_builtin_module_test.py rename to tests/input/func_noerror_builtin_module_test.py diff --git a/pylint/test/input/func_noerror_class_attributes.py b/tests/input/func_noerror_class_attributes.py similarity index 100% rename from pylint/test/input/func_noerror_class_attributes.py rename to tests/input/func_noerror_class_attributes.py diff --git a/pylint/test/input/func_noerror_classes_meth_could_be_a_function.py b/tests/input/func_noerror_classes_meth_could_be_a_function.py similarity index 100% rename from pylint/test/input/func_noerror_classes_meth_could_be_a_function.py rename to tests/input/func_noerror_classes_meth_could_be_a_function.py diff --git a/pylint/test/input/func_noerror_classes_protected_member_access.py b/tests/input/func_noerror_classes_protected_member_access.py similarity index 100% rename from pylint/test/input/func_noerror_classes_protected_member_access.py rename to tests/input/func_noerror_classes_protected_member_access.py diff --git a/pylint/test/input/func_noerror_decorator_scope.py b/tests/input/func_noerror_decorator_scope.py similarity index 100% rename from pylint/test/input/func_noerror_decorator_scope.py rename to tests/input/func_noerror_decorator_scope.py diff --git a/pylint/test/input/func_noerror_e1101_9588_base_attr_aug_assign.py b/tests/input/func_noerror_e1101_9588_base_attr_aug_assign.py similarity index 100% rename from pylint/test/input/func_noerror_e1101_9588_base_attr_aug_assign.py rename to tests/input/func_noerror_e1101_9588_base_attr_aug_assign.py diff --git a/pylint/test/input/func_noerror_external_classmethod_crash.py b/tests/input/func_noerror_external_classmethod_crash.py similarity index 100% rename from pylint/test/input/func_noerror_external_classmethod_crash.py rename to tests/input/func_noerror_external_classmethod_crash.py diff --git a/pylint/test/input/func_noerror_inner_classes.py b/tests/input/func_noerror_inner_classes.py similarity index 100% rename from pylint/test/input/func_noerror_inner_classes.py rename to tests/input/func_noerror_inner_classes.py diff --git a/pylint/test/input/func_noerror_lambda_use_before_assign.py b/tests/input/func_noerror_lambda_use_before_assign.py similarity index 100% rename from pylint/test/input/func_noerror_lambda_use_before_assign.py rename to tests/input/func_noerror_lambda_use_before_assign.py diff --git a/pylint/test/input/func_noerror_mcs_attr_access.py b/tests/input/func_noerror_mcs_attr_access.py similarity index 100% rename from pylint/test/input/func_noerror_mcs_attr_access.py rename to tests/input/func_noerror_mcs_attr_access.py diff --git a/pylint/test/input/func_noerror_new_style_class_py_30.py b/tests/input/func_noerror_new_style_class_py_30.py similarity index 100% rename from pylint/test/input/func_noerror_new_style_class_py_30.py rename to tests/input/func_noerror_new_style_class_py_30.py diff --git a/pylint/test/input/func_noerror_no_warning_docstring.py b/tests/input/func_noerror_no_warning_docstring.py similarity index 100% rename from pylint/test/input/func_noerror_no_warning_docstring.py rename to tests/input/func_noerror_no_warning_docstring.py diff --git a/pylint/test/input/func_noerror_object_as_class_attribute.py b/tests/input/func_noerror_object_as_class_attribute.py similarity index 100% rename from pylint/test/input/func_noerror_object_as_class_attribute.py rename to tests/input/func_noerror_object_as_class_attribute.py diff --git a/pylint/test/input/func_noerror_overloaded_operator.py b/tests/input/func_noerror_overloaded_operator.py similarity index 100% rename from pylint/test/input/func_noerror_overloaded_operator.py rename to tests/input/func_noerror_overloaded_operator.py diff --git a/pylint/test/input/func_noerror_property_affectation_py26.py b/tests/input/func_noerror_property_affectation_py26.py similarity index 100% rename from pylint/test/input/func_noerror_property_affectation_py26.py rename to tests/input/func_noerror_property_affectation_py26.py diff --git a/pylint/test/input/func_noerror_yield_assign_py25.py b/tests/input/func_noerror_yield_assign_py25.py similarity index 100% rename from pylint/test/input/func_noerror_yield_assign_py25.py rename to tests/input/func_noerror_yield_assign_py25.py diff --git a/pylint/test/input/func_noerror_yield_return_mix.py b/tests/input/func_noerror_yield_return_mix.py similarity index 100% rename from pylint/test/input/func_noerror_yield_return_mix.py rename to tests/input/func_noerror_yield_return_mix.py diff --git a/pylint/test/input/func_nonregr___file___global.py b/tests/input/func_nonregr___file___global.py similarity index 100% rename from pylint/test/input/func_nonregr___file___global.py rename to tests/input/func_nonregr___file___global.py diff --git a/pylint/test/input/func_return_yield_mix_py_33.py b/tests/input/func_return_yield_mix_py_33.py similarity index 100% rename from pylint/test/input/func_return_yield_mix_py_33.py rename to tests/input/func_return_yield_mix_py_33.py diff --git a/pylint/test/input/func_typecheck_callfunc_assigment.py b/tests/input/func_typecheck_callfunc_assigment.py similarity index 100% rename from pylint/test/input/func_typecheck_callfunc_assigment.py rename to tests/input/func_typecheck_callfunc_assigment.py diff --git a/pylint/test/input/func_unused_import_py30.py b/tests/input/func_unused_import_py30.py similarity index 100% rename from pylint/test/input/func_unused_import_py30.py rename to tests/input/func_unused_import_py30.py diff --git a/pylint/test/input/func_variables_unused_name_from_wilcard_import.py b/tests/input/func_variables_unused_name_from_wilcard_import.py similarity index 100% rename from pylint/test/input/func_variables_unused_name_from_wilcard_import.py rename to tests/input/func_variables_unused_name_from_wilcard_import.py diff --git a/pylint/test/input/func_w0122_py_30.py b/tests/input/func_w0122_py_30.py similarity index 100% rename from pylint/test/input/func_w0122_py_30.py rename to tests/input/func_w0122_py_30.py diff --git a/pylint/test/input/func_w0233.py b/tests/input/func_w0233.py similarity index 100% rename from pylint/test/input/func_w0233.py rename to tests/input/func_w0233.py diff --git a/pylint/test/input/func_w0332_py_30.py b/tests/input/func_w0332_py_30.py similarity index 100% rename from pylint/test/input/func_w0332_py_30.py rename to tests/input/func_w0332_py_30.py diff --git a/pylint/test/input/func_w0401.py b/tests/input/func_w0401.py similarity index 100% rename from pylint/test/input/func_w0401.py rename to tests/input/func_w0401.py diff --git a/pylint/test/input/func_w0401_disabled.py b/tests/input/func_w0401_disabled.py similarity index 100% rename from pylint/test/input/func_w0401_disabled.py rename to tests/input/func_w0401_disabled.py diff --git a/pylint/test/input/func_w0401_disabled_in_func.py b/tests/input/func_w0401_disabled_in_func.py similarity index 100% rename from pylint/test/input/func_w0401_disabled_in_func.py rename to tests/input/func_w0401_disabled_in_func.py diff --git a/pylint/test/input/func_w0401_package/__init__.py b/tests/input/func_w0401_package/__init__.py similarity index 100% rename from pylint/test/input/func_w0401_package/__init__.py rename to tests/input/func_w0401_package/__init__.py diff --git a/pylint/test/input/func_w0401_package/all_the_things.py b/tests/input/func_w0401_package/all_the_things.py similarity index 100% rename from pylint/test/input/func_w0401_package/all_the_things.py rename to tests/input/func_w0401_package/all_the_things.py diff --git a/pylint/test/input/func_w0401_package/thing1.py b/tests/input/func_w0401_package/thing1.py similarity index 100% rename from pylint/test/input/func_w0401_package/thing1.py rename to tests/input/func_w0401_package/thing1.py diff --git a/pylint/test/input/func_w0401_package/thing2.py b/tests/input/func_w0401_package/thing2.py similarity index 100% rename from pylint/test/input/func_w0401_package/thing2.py rename to tests/input/func_w0401_package/thing2.py diff --git a/pylint/test/input/func_w0404.py b/tests/input/func_w0404.py similarity index 100% rename from pylint/test/input/func_w0404.py rename to tests/input/func_w0404.py diff --git a/pylint/test/input/func_w0405.py b/tests/input/func_w0405.py similarity index 100% rename from pylint/test/input/func_w0405.py rename to tests/input/func_w0405.py diff --git a/pylint/test/input/func_w0406.py b/tests/input/func_w0406.py similarity index 100% rename from pylint/test/input/func_w0406.py rename to tests/input/func_w0406.py diff --git a/pylint/test/input/func_w0611.py b/tests/input/func_w0611.py similarity index 100% rename from pylint/test/input/func_w0611.py rename to tests/input/func_w0611.py diff --git a/pylint/test/input/func_w0612.py b/tests/input/func_w0612.py similarity index 100% rename from pylint/test/input/func_w0612.py rename to tests/input/func_w0612.py diff --git a/pylint/test/input/func_w0613.py b/tests/input/func_w0613.py similarity index 100% rename from pylint/test/input/func_w0613.py rename to tests/input/func_w0613.py diff --git a/pylint/test/input/func_w0623_py30.py b/tests/input/func_w0623_py30.py similarity index 100% rename from pylint/test/input/func_w0623_py30.py rename to tests/input/func_w0623_py30.py diff --git a/pylint/test/input/func_w0801.py b/tests/input/func_w0801.py similarity index 100% rename from pylint/test/input/func_w0801.py rename to tests/input/func_w0801.py diff --git a/pylint/test/input/hide_code_with_imports.py b/tests/input/hide_code_with_imports.py similarity index 100% rename from pylint/test/input/hide_code_with_imports.py rename to tests/input/hide_code_with_imports.py diff --git a/pylint/test/input/ignore_except_pass_by_default.py b/tests/input/ignore_except_pass_by_default.py similarity index 100% rename from pylint/test/input/ignore_except_pass_by_default.py rename to tests/input/ignore_except_pass_by_default.py diff --git a/pylint/test/input/multiline-import b/tests/input/multiline-import similarity index 100% rename from pylint/test/input/multiline-import rename to tests/input/multiline-import diff --git a/pylint/test/input/noext b/tests/input/noext similarity index 100% rename from pylint/test/input/noext rename to tests/input/noext diff --git a/pylint/test/input/not__init__.py b/tests/input/not__init__.py similarity index 100% rename from pylint/test/input/not__init__.py rename to tests/input/not__init__.py diff --git a/pylint/test/input/similar1 b/tests/input/similar1 similarity index 100% rename from pylint/test/input/similar1 rename to tests/input/similar1 diff --git a/pylint/test/input/similar2 b/tests/input/similar2 similarity index 100% rename from pylint/test/input/similar2 rename to tests/input/similar2 diff --git a/pylint/test/input/w0401_cycle.py b/tests/input/w0401_cycle.py similarity index 100% rename from pylint/test/input/w0401_cycle.py rename to tests/input/w0401_cycle.py diff --git a/pylint/test/input/w0801_same.py b/tests/input/w0801_same.py similarity index 100% rename from pylint/test/input/w0801_same.py rename to tests/input/w0801_same.py diff --git a/pylint/test/message/unittest_message.py b/tests/message/unittest_message.py similarity index 100% rename from pylint/test/message/unittest_message.py rename to tests/message/unittest_message.py diff --git a/pylint/test/message/unittest_message_store.py b/tests/message/unittest_message_store.py similarity index 100% rename from pylint/test/message/unittest_message_store.py rename to tests/message/unittest_message_store.py diff --git a/pylint/test/messages/builtin_module.txt b/tests/messages/builtin_module.txt similarity index 100% rename from pylint/test/messages/builtin_module.txt rename to tests/messages/builtin_module.txt diff --git a/pylint/test/messages/func_3k_removed_stuff_py_30.txt b/tests/messages/func_3k_removed_stuff_py_30.txt similarity index 100% rename from pylint/test/messages/func_3k_removed_stuff_py_30.txt rename to tests/messages/func_3k_removed_stuff_py_30.txt diff --git a/pylint/test/messages/func_bad_cont_dictcomp_py27.txt b/tests/messages/func_bad_cont_dictcomp_py27.txt similarity index 100% rename from pylint/test/messages/func_bad_cont_dictcomp_py27.txt rename to tests/messages/func_bad_cont_dictcomp_py27.txt diff --git a/pylint/test/messages/func_bug113231.txt b/tests/messages/func_bug113231.txt similarity index 100% rename from pylint/test/messages/func_bug113231.txt rename to tests/messages/func_bug113231.txt diff --git a/pylint/test/messages/func_disable_linebased.txt b/tests/messages/func_disable_linebased.txt similarity index 100% rename from pylint/test/messages/func_disable_linebased.txt rename to tests/messages/func_disable_linebased.txt diff --git a/pylint/test/messages/func_disable_linebased_py30.txt b/tests/messages/func_disable_linebased_py30.txt similarity index 100% rename from pylint/test/messages/func_disable_linebased_py30.txt rename to tests/messages/func_disable_linebased_py30.txt diff --git a/pylint/test/messages/func_dotted_ancestor.txt b/tests/messages/func_dotted_ancestor.txt similarity index 100% rename from pylint/test/messages/func_dotted_ancestor.txt rename to tests/messages/func_dotted_ancestor.txt diff --git a/pylint/test/messages/func_e0012.txt b/tests/messages/func_e0012.txt similarity index 100% rename from pylint/test/messages/func_e0012.txt rename to tests/messages/func_e0012.txt diff --git a/pylint/test/messages/func_e0204.txt b/tests/messages/func_e0204.txt similarity index 100% rename from pylint/test/messages/func_e0204.txt rename to tests/messages/func_e0204.txt diff --git a/pylint/test/messages/func_e12xx.txt b/tests/messages/func_e12xx.txt similarity index 100% rename from pylint/test/messages/func_e12xx.txt rename to tests/messages/func_e12xx.txt diff --git a/pylint/test/messages/func_e13xx.txt b/tests/messages/func_e13xx.txt similarity index 100% rename from pylint/test/messages/func_e13xx.txt rename to tests/messages/func_e13xx.txt diff --git a/pylint/test/messages/func_e13xx_py30.txt b/tests/messages/func_e13xx_py30.txt similarity index 100% rename from pylint/test/messages/func_e13xx_py30.txt rename to tests/messages/func_e13xx_py30.txt diff --git a/pylint/test/messages/func_excess_escapes.txt b/tests/messages/func_excess_escapes.txt similarity index 100% rename from pylint/test/messages/func_excess_escapes.txt rename to tests/messages/func_excess_escapes.txt diff --git a/pylint/test/messages/func_first_arg.txt b/tests/messages/func_first_arg.txt similarity index 100% rename from pylint/test/messages/func_first_arg.txt rename to tests/messages/func_first_arg.txt diff --git a/pylint/test/messages/func_i0011.txt b/tests/messages/func_i0011.txt similarity index 100% rename from pylint/test/messages/func_i0011.txt rename to tests/messages/func_i0011.txt diff --git a/pylint/test/messages/func_i0012.txt b/tests/messages/func_i0012.txt similarity index 100% rename from pylint/test/messages/func_i0012.txt rename to tests/messages/func_i0012.txt diff --git a/pylint/test/messages/func_i0013.txt b/tests/messages/func_i0013.txt similarity index 100% rename from pylint/test/messages/func_i0013.txt rename to tests/messages/func_i0013.txt diff --git a/pylint/test/messages/func_i0014.txt b/tests/messages/func_i0014.txt similarity index 100% rename from pylint/test/messages/func_i0014.txt rename to tests/messages/func_i0014.txt diff --git a/pylint/test/messages/func_i0020.txt b/tests/messages/func_i0020.txt similarity index 100% rename from pylint/test/messages/func_i0020.txt rename to tests/messages/func_i0020.txt diff --git a/pylint/test/messages/func_i0022.txt b/tests/messages/func_i0022.txt similarity index 100% rename from pylint/test/messages/func_i0022.txt rename to tests/messages/func_i0022.txt diff --git a/pylint/test/messages/func_logging_not_lazy_with_logger.txt b/tests/messages/func_logging_not_lazy_with_logger.txt similarity index 100% rename from pylint/test/messages/func_logging_not_lazy_with_logger.txt rename to tests/messages/func_logging_not_lazy_with_logger.txt diff --git a/pylint/test/messages/func_loopvar_in_dict_comp_py27.txt b/tests/messages/func_loopvar_in_dict_comp_py27.txt similarity index 100% rename from pylint/test/messages/func_loopvar_in_dict_comp_py27.txt rename to tests/messages/func_loopvar_in_dict_comp_py27.txt diff --git a/pylint/test/messages/func_module___dict__.txt b/tests/messages/func_module___dict__.txt similarity index 100% rename from pylint/test/messages/func_module___dict__.txt rename to tests/messages/func_module___dict__.txt diff --git a/pylint/test/messages/func_nameerror_on_string_substitution.txt b/tests/messages/func_nameerror_on_string_substitution.txt similarity index 100% rename from pylint/test/messages/func_nameerror_on_string_substitution.txt rename to tests/messages/func_nameerror_on_string_substitution.txt diff --git a/pylint/test/messages/func_no_dummy_redefined.txt b/tests/messages/func_no_dummy_redefined.txt similarity index 100% rename from pylint/test/messages/func_no_dummy_redefined.txt rename to tests/messages/func_no_dummy_redefined.txt diff --git a/pylint/test/messages/func_nonregr___file___global.txt b/tests/messages/func_nonregr___file___global.txt similarity index 100% rename from pylint/test/messages/func_nonregr___file___global.txt rename to tests/messages/func_nonregr___file___global.txt diff --git a/pylint/test/messages/func_raw_escapes.txt b/tests/messages/func_raw_escapes.txt similarity index 100% rename from pylint/test/messages/func_raw_escapes.txt rename to tests/messages/func_raw_escapes.txt diff --git a/pylint/test/messages/func_return_yield_mix_py_33.txt b/tests/messages/func_return_yield_mix_py_33.txt similarity index 100% rename from pylint/test/messages/func_return_yield_mix_py_33.txt rename to tests/messages/func_return_yield_mix_py_33.txt diff --git a/pylint/test/messages/func_toolonglines_py30.txt b/tests/messages/func_toolonglines_py30.txt similarity index 100% rename from pylint/test/messages/func_toolonglines_py30.txt rename to tests/messages/func_toolonglines_py30.txt diff --git a/pylint/test/messages/func_typecheck_callfunc_assigment.txt b/tests/messages/func_typecheck_callfunc_assigment.txt similarity index 100% rename from pylint/test/messages/func_typecheck_callfunc_assigment.txt rename to tests/messages/func_typecheck_callfunc_assigment.txt diff --git a/pylint/test/messages/func_typecheck_getattr_py30.txt b/tests/messages/func_typecheck_getattr_py30.txt similarity index 100% rename from pylint/test/messages/func_typecheck_getattr_py30.txt rename to tests/messages/func_typecheck_getattr_py30.txt diff --git a/pylint/test/messages/func_typecheck_non_callable_call.txt b/tests/messages/func_typecheck_non_callable_call.txt similarity index 100% rename from pylint/test/messages/func_typecheck_non_callable_call.txt rename to tests/messages/func_typecheck_non_callable_call.txt diff --git a/pylint/test/messages/func_unicode_literal_py26.txt b/tests/messages/func_unicode_literal_py26.txt similarity index 100% rename from pylint/test/messages/func_unicode_literal_py26.txt rename to tests/messages/func_unicode_literal_py26.txt diff --git a/pylint/test/messages/func_unicode_literal_py274.txt b/tests/messages/func_unicode_literal_py274.txt similarity index 100% rename from pylint/test/messages/func_unicode_literal_py274.txt rename to tests/messages/func_unicode_literal_py274.txt diff --git a/pylint/test/messages/func_unused_import_py30.txt b/tests/messages/func_unused_import_py30.txt similarity index 100% rename from pylint/test/messages/func_unused_import_py30.txt rename to tests/messages/func_unused_import_py30.txt diff --git a/pylint/test/messages/func_use_for_or_listcomp_var_py29.txt b/tests/messages/func_use_for_or_listcomp_var_py29.txt similarity index 100% rename from pylint/test/messages/func_use_for_or_listcomp_var_py29.txt rename to tests/messages/func_use_for_or_listcomp_var_py29.txt diff --git a/pylint/test/messages/func_use_for_or_listcomp_var_py30.txt b/tests/messages/func_use_for_or_listcomp_var_py30.txt similarity index 100% rename from pylint/test/messages/func_use_for_or_listcomp_var_py30.txt rename to tests/messages/func_use_for_or_listcomp_var_py30.txt diff --git a/pylint/test/messages/func_variables_unused_name_from_wilcard_import.txt b/tests/messages/func_variables_unused_name_from_wilcard_import.txt similarity index 100% rename from pylint/test/messages/func_variables_unused_name_from_wilcard_import.txt rename to tests/messages/func_variables_unused_name_from_wilcard_import.txt diff --git a/pylint/test/messages/func_w0122_py_30.txt b/tests/messages/func_w0122_py_30.txt similarity index 100% rename from pylint/test/messages/func_w0122_py_30.txt rename to tests/messages/func_w0122_py_30.txt diff --git a/pylint/test/messages/func_w0233.txt b/tests/messages/func_w0233.txt similarity index 100% rename from pylint/test/messages/func_w0233.txt rename to tests/messages/func_w0233.txt diff --git a/pylint/test/messages/func_w0312.txt b/tests/messages/func_w0312.txt similarity index 100% rename from pylint/test/messages/func_w0312.txt rename to tests/messages/func_w0312.txt diff --git a/pylint/test/messages/func_w0332_py_30.txt b/tests/messages/func_w0332_py_30.txt similarity index 100% rename from pylint/test/messages/func_w0332_py_30.txt rename to tests/messages/func_w0332_py_30.txt diff --git a/pylint/test/messages/func_w0401.txt b/tests/messages/func_w0401.txt similarity index 100% rename from pylint/test/messages/func_w0401.txt rename to tests/messages/func_w0401.txt diff --git a/pylint/test/messages/func_w0401_disabled.txt b/tests/messages/func_w0401_disabled.txt similarity index 100% rename from pylint/test/messages/func_w0401_disabled.txt rename to tests/messages/func_w0401_disabled.txt diff --git a/pylint/test/messages/func_w0401_disabled_in_func.txt b/tests/messages/func_w0401_disabled_in_func.txt similarity index 100% rename from pylint/test/messages/func_w0401_disabled_in_func.txt rename to tests/messages/func_w0401_disabled_in_func.txt diff --git a/pylint/test/messages/func_w0401_package.txt b/tests/messages/func_w0401_package.txt similarity index 100% rename from pylint/test/messages/func_w0401_package.txt rename to tests/messages/func_w0401_package.txt diff --git a/pylint/test/messages/func_w0404.txt b/tests/messages/func_w0404.txt similarity index 100% rename from pylint/test/messages/func_w0404.txt rename to tests/messages/func_w0404.txt diff --git a/pylint/test/messages/func_w0405.txt b/tests/messages/func_w0405.txt similarity index 100% rename from pylint/test/messages/func_w0405.txt rename to tests/messages/func_w0405.txt diff --git a/pylint/test/messages/func_w0406.txt b/tests/messages/func_w0406.txt similarity index 100% rename from pylint/test/messages/func_w0406.txt rename to tests/messages/func_w0406.txt diff --git a/pylint/test/messages/func_w0611.txt b/tests/messages/func_w0611.txt similarity index 100% rename from pylint/test/messages/func_w0611.txt rename to tests/messages/func_w0611.txt diff --git a/pylint/test/messages/func_w0612.txt b/tests/messages/func_w0612.txt similarity index 100% rename from pylint/test/messages/func_w0612.txt rename to tests/messages/func_w0612.txt diff --git a/pylint/test/messages/func_w0613.txt b/tests/messages/func_w0613.txt similarity index 100% rename from pylint/test/messages/func_w0613.txt rename to tests/messages/func_w0613.txt diff --git a/pylint/test/messages/func_w0622.txt b/tests/messages/func_w0622.txt similarity index 100% rename from pylint/test/messages/func_w0622.txt rename to tests/messages/func_w0622.txt diff --git a/pylint/test/messages/func_w0623.txt b/tests/messages/func_w0623.txt similarity index 100% rename from pylint/test/messages/func_w0623.txt rename to tests/messages/func_w0623.txt diff --git a/pylint/test/messages/func_w0623_py30.txt b/tests/messages/func_w0623_py30.txt similarity index 100% rename from pylint/test/messages/func_w0623_py30.txt rename to tests/messages/func_w0623_py30.txt diff --git a/pylint/test/messages/func_w0623_py_30.txt b/tests/messages/func_w0623_py_30.txt similarity index 100% rename from pylint/test/messages/func_w0623_py_30.txt rename to tests/messages/func_w0623_py_30.txt diff --git a/pylint/test/messages/func_w0801.txt b/tests/messages/func_w0801.txt similarity index 100% rename from pylint/test/messages/func_w0801.txt rename to tests/messages/func_w0801.txt diff --git a/pylint/test/messages/func_with_without_as_py25.txt b/tests/messages/func_with_without_as_py25.txt similarity index 100% rename from pylint/test/messages/func_with_without_as_py25.txt rename to tests/messages/func_with_without_as_py25.txt diff --git a/pylint/test/regrtest_data/.pylintrc b/tests/regrtest_data/.pylintrc similarity index 100% rename from pylint/test/regrtest_data/.pylintrc rename to tests/regrtest_data/.pylintrc diff --git a/pylint/test/regrtest_data/absimp/__init__.py b/tests/regrtest_data/absimp/__init__.py similarity index 100% rename from pylint/test/regrtest_data/absimp/__init__.py rename to tests/regrtest_data/absimp/__init__.py diff --git a/pylint/test/regrtest_data/absimp/string.py b/tests/regrtest_data/absimp/string.py similarity index 100% rename from pylint/test/regrtest_data/absimp/string.py rename to tests/regrtest_data/absimp/string.py diff --git a/pylint/test/regrtest_data/application_crash.py b/tests/regrtest_data/application_crash.py similarity index 100% rename from pylint/test/regrtest_data/application_crash.py rename to tests/regrtest_data/application_crash.py diff --git a/pylint/test/regrtest_data/bad_package/__init__.py b/tests/regrtest_data/bad_package/__init__.py similarity index 100% rename from pylint/test/regrtest_data/bad_package/__init__.py rename to tests/regrtest_data/bad_package/__init__.py diff --git a/pylint/test/regrtest_data/bad_package/wrong.py b/tests/regrtest_data/bad_package/wrong.py similarity index 100% rename from pylint/test/regrtest_data/bad_package/wrong.py rename to tests/regrtest_data/bad_package/wrong.py diff --git a/pylint/test/regrtest_data/beyond_top/__init__.py b/tests/regrtest_data/beyond_top/__init__.py similarity index 100% rename from pylint/test/regrtest_data/beyond_top/__init__.py rename to tests/regrtest_data/beyond_top/__init__.py diff --git a/pylint/test/regrtest_data/beyond_top/data.py b/tests/regrtest_data/beyond_top/data.py similarity index 100% rename from pylint/test/regrtest_data/beyond_top/data.py rename to tests/regrtest_data/beyond_top/data.py diff --git a/pylint/test/regrtest_data/classdoc_usage.py b/tests/regrtest_data/classdoc_usage.py similarity index 100% rename from pylint/test/regrtest_data/classdoc_usage.py rename to tests/regrtest_data/classdoc_usage.py diff --git a/pylint/test/regrtest_data/comments_pylintrc b/tests/regrtest_data/comments_pylintrc similarity index 100% rename from pylint/test/regrtest_data/comments_pylintrc rename to tests/regrtest_data/comments_pylintrc diff --git a/pylint/test/regrtest_data/decimal_inference.py b/tests/regrtest_data/decimal_inference.py similarity index 100% rename from pylint/test/regrtest_data/decimal_inference.py rename to tests/regrtest_data/decimal_inference.py diff --git a/pylint/test/regrtest_data/descriptor_crash.py b/tests/regrtest_data/descriptor_crash.py similarity index 100% rename from pylint/test/regrtest_data/descriptor_crash.py rename to tests/regrtest_data/descriptor_crash.py diff --git a/pylint/test/regrtest_data/dummy/__init__.py b/tests/regrtest_data/dummy/__init__.py similarity index 100% rename from pylint/test/regrtest_data/dummy/__init__.py rename to tests/regrtest_data/dummy/__init__.py diff --git a/pylint/test/regrtest_data/dummy/another.py b/tests/regrtest_data/dummy/another.py similarity index 100% rename from pylint/test/regrtest_data/dummy/another.py rename to tests/regrtest_data/dummy/another.py diff --git a/pylint/test/regrtest_data/dummy/dummy.py b/tests/regrtest_data/dummy/dummy.py similarity index 100% rename from pylint/test/regrtest_data/dummy/dummy.py rename to tests/regrtest_data/dummy/dummy.py diff --git a/pylint/test/regrtest_data/dummy_plugin.rc b/tests/regrtest_data/dummy_plugin.rc similarity index 100% rename from pylint/test/regrtest_data/dummy_plugin.rc rename to tests/regrtest_data/dummy_plugin.rc diff --git a/pylint/test/regrtest_data/dummy_plugin/dummy_conf_plugin.py b/tests/regrtest_data/dummy_plugin/dummy_conf_plugin.py similarity index 100% rename from pylint/test/regrtest_data/dummy_plugin/dummy_conf_plugin.py rename to tests/regrtest_data/dummy_plugin/dummy_conf_plugin.py diff --git a/pylint/test/regrtest_data/dummy_plugin/dummy_plugin.py b/tests/regrtest_data/dummy_plugin/dummy_plugin.py similarity index 100% rename from pylint/test/regrtest_data/dummy_plugin/dummy_plugin.py rename to tests/regrtest_data/dummy_plugin/dummy_plugin.py diff --git a/pylint/test/regrtest_data/empty.py b/tests/regrtest_data/empty.py similarity index 100% rename from pylint/test/regrtest_data/empty.py rename to tests/regrtest_data/empty.py diff --git a/pylint/test/regrtest_data/func_block_disable_msg.py b/tests/regrtest_data/func_block_disable_msg.py similarity index 100% rename from pylint/test/regrtest_data/func_block_disable_msg.py rename to tests/regrtest_data/func_block_disable_msg.py diff --git a/pylint/test/regrtest_data/import_assign.py b/tests/regrtest_data/import_assign.py similarity index 100% rename from pylint/test/regrtest_data/import_assign.py rename to tests/regrtest_data/import_assign.py diff --git a/pylint/test/regrtest_data/import_package_subpackage_module.py b/tests/regrtest_data/import_package_subpackage_module.py similarity index 100% rename from pylint/test/regrtest_data/import_package_subpackage_module.py rename to tests/regrtest_data/import_package_subpackage_module.py diff --git a/pylint/test/regrtest_data/import_something.py b/tests/regrtest_data/import_something.py similarity index 100% rename from pylint/test/regrtest_data/import_something.py rename to tests/regrtest_data/import_something.py diff --git a/pylint/test/regrtest_data/init_wildcard/__init__.py b/tests/regrtest_data/init_wildcard/__init__.py similarity index 100% rename from pylint/test/regrtest_data/init_wildcard/__init__.py rename to tests/regrtest_data/init_wildcard/__init__.py diff --git a/pylint/test/regrtest_data/meta.py b/tests/regrtest_data/meta.py similarity index 100% rename from pylint/test/regrtest_data/meta.py rename to tests/regrtest_data/meta.py diff --git a/pylint/test/regrtest_data/module_global.py b/tests/regrtest_data/module_global.py similarity index 100% rename from pylint/test/regrtest_data/module_global.py rename to tests/regrtest_data/module_global.py diff --git a/pylint/test/regrtest_data/no_stdout_encoding.py b/tests/regrtest_data/no_stdout_encoding.py similarity index 100% rename from pylint/test/regrtest_data/no_stdout_encoding.py rename to tests/regrtest_data/no_stdout_encoding.py diff --git a/pylint/test/regrtest_data/numarray_import.py b/tests/regrtest_data/numarray_import.py similarity index 100% rename from pylint/test/regrtest_data/numarray_import.py rename to tests/regrtest_data/numarray_import.py diff --git a/pylint/test/regrtest_data/numarray_inf.py b/tests/regrtest_data/numarray_inf.py similarity index 100% rename from pylint/test/regrtest_data/numarray_inf.py rename to tests/regrtest_data/numarray_inf.py diff --git a/pylint/test/regrtest_data/package/AudioTime.py b/tests/regrtest_data/package/AudioTime.py similarity index 100% rename from pylint/test/regrtest_data/package/AudioTime.py rename to tests/regrtest_data/package/AudioTime.py diff --git a/pylint/test/regrtest_data/package/__init__.py b/tests/regrtest_data/package/__init__.py similarity index 100% rename from pylint/test/regrtest_data/package/__init__.py rename to tests/regrtest_data/package/__init__.py diff --git a/pylint/test/regrtest_data/package/subpackage/__init__.py b/tests/regrtest_data/package/subpackage/__init__.py similarity index 100% rename from pylint/test/regrtest_data/package/subpackage/__init__.py rename to tests/regrtest_data/package/subpackage/__init__.py diff --git a/pylint/test/regrtest_data/package/subpackage/module.py b/tests/regrtest_data/package/subpackage/module.py similarity index 100% rename from pylint/test/regrtest_data/package/subpackage/module.py rename to tests/regrtest_data/package/subpackage/module.py diff --git a/pylint/test/regrtest_data/package_all/__init__.py b/tests/regrtest_data/package_all/__init__.py similarity index 100% rename from pylint/test/regrtest_data/package_all/__init__.py rename to tests/regrtest_data/package_all/__init__.py diff --git a/pylint/test/regrtest_data/package_all/notmissing.py b/tests/regrtest_data/package_all/notmissing.py similarity index 100% rename from pylint/test/regrtest_data/package_all/notmissing.py rename to tests/regrtest_data/package_all/notmissing.py diff --git a/pylint/test/regrtest_data/precedence_test.py b/tests/regrtest_data/precedence_test.py similarity index 100% rename from pylint/test/regrtest_data/precedence_test.py rename to tests/regrtest_data/precedence_test.py diff --git a/pylint/test/regrtest_data/py3k-disabled.rc b/tests/regrtest_data/py3k-disabled.rc similarity index 100% rename from pylint/test/regrtest_data/py3k-disabled.rc rename to tests/regrtest_data/py3k-disabled.rc diff --git a/pylint/test/regrtest_data/py3k_error_flag.py b/tests/regrtest_data/py3k_error_flag.py similarity index 100% rename from pylint/test/regrtest_data/py3k_error_flag.py rename to tests/regrtest_data/py3k_error_flag.py diff --git a/pylint/test/regrtest_data/py3k_errors_and_warnings.py b/tests/regrtest_data/py3k_errors_and_warnings.py similarity index 100% rename from pylint/test/regrtest_data/py3k_errors_and_warnings.py rename to tests/regrtest_data/py3k_errors_and_warnings.py diff --git a/pylint/test/regrtest_data/special_attr_scope_lookup_crash.py b/tests/regrtest_data/special_attr_scope_lookup_crash.py similarity index 100% rename from pylint/test/regrtest_data/special_attr_scope_lookup_crash.py rename to tests/regrtest_data/special_attr_scope_lookup_crash.py diff --git a/pylint/test/regrtest_data/syntax_error.py b/tests/regrtest_data/syntax_error.py similarity index 100% rename from pylint/test/regrtest_data/syntax_error.py rename to tests/regrtest_data/syntax_error.py diff --git a/pylint/test/regrtest_data/test_pylintrc_comments.py b/tests/regrtest_data/test_pylintrc_comments.py similarity index 100% rename from pylint/test/regrtest_data/test_pylintrc_comments.py rename to tests/regrtest_data/test_pylintrc_comments.py diff --git a/pylint/test/regrtest_data/try_finally_disable_msg_crash.py b/tests/regrtest_data/try_finally_disable_msg_crash.py similarity index 100% rename from pylint/test/regrtest_data/try_finally_disable_msg_crash.py rename to tests/regrtest_data/try_finally_disable_msg_crash.py diff --git a/pylint/test/regrtest_data/unused_variable.py b/tests/regrtest_data/unused_variable.py similarity index 100% rename from pylint/test/regrtest_data/unused_variable.py rename to tests/regrtest_data/unused_variable.py diff --git a/pylint/test/regrtest_data/wildcard.py b/tests/regrtest_data/wildcard.py similarity index 100% rename from pylint/test/regrtest_data/wildcard.py rename to tests/regrtest_data/wildcard.py diff --git a/pylint/test/regrtest_data/wrong_import_position.py b/tests/regrtest_data/wrong_import_position.py similarity index 100% rename from pylint/test/regrtest_data/wrong_import_position.py rename to tests/regrtest_data/wrong_import_position.py diff --git a/pylint/test/test_func.py b/tests/test_func.py similarity index 100% rename from pylint/test/test_func.py rename to tests/test_func.py diff --git a/pylint/test/test_functional.py b/tests/test_functional.py similarity index 100% rename from pylint/test/test_functional.py rename to tests/test_functional.py diff --git a/pylint/test/test_import_graph.py b/tests/test_import_graph.py similarity index 100% rename from pylint/test/test_import_graph.py rename to tests/test_import_graph.py diff --git a/pylint/test/test_regr.py b/tests/test_regr.py similarity index 100% rename from pylint/test/test_regr.py rename to tests/test_regr.py diff --git a/pylint/test/test_self.py b/tests/test_self.py similarity index 94% rename from pylint/test/test_self.py rename to tests/test_self.py index f90a893404..051eea87d7 100644 --- a/pylint/test/test_self.py +++ b/tests/test_self.py @@ -339,24 +339,24 @@ def test_enable_all_works(self): expected = textwrap.dedent( """ ************* Module data.clientmodule_test - pylint/test/data/clientmodule_test.py:10:8: W0612: Unused variable 'local_variable' (unused-variable) - pylint/test/data/clientmodule_test.py:18:4: C0111: Missing method docstring (missing-docstring) - pylint/test/data/clientmodule_test.py:22:0: C0111: Missing class docstring (missing-docstring) - """ + {0}:10:8: W0612: Unused variable 'local_variable' (unused-variable) + {0}:18:4: C0111: Missing method docstring (missing-docstring) + {0}:22:0: C0111: Missing class docstring (missing-docstring) + """.format(module) ) self._test_output( [module, "--disable=all", "--enable=all", "-rn"], expected_output=expected ) def test_wrong_import_position_when_others_disabled(self): + module1 = join(HERE, "regrtest_data", "import_something.py") + module2 = join(HERE, "regrtest_data", "wrong_import_position.py") expected_output = textwrap.dedent( """ ************* Module wrong_import_position - pylint/test/regrtest_data/wrong_import_position.py:11:0: C0413: Import "import os" should be placed at the top of the module (wrong-import-position) - """ + {}:11:0: C0413: Import "import os" should be placed at the top of the module (wrong-import-position) + """.format(module2) ) - module1 = join(HERE, "regrtest_data", "import_something.py") - module2 = join(HERE, "regrtest_data", "wrong_import_position.py") args = [ module2, module1, @@ -462,13 +462,13 @@ def test_information_category_disabled_by_default(self): self._test_output([path], expected_output=expected) def test_error_mode_shows_no_score(self): + module = join(HERE, "regrtest_data", "application_crash.py") expected_output = textwrap.dedent( """ ************* Module application_crash - pylint/test/regrtest_data/application_crash.py:1:6: E0602: Undefined variable 'something_undefined' (undefined-variable) - """ + {}:1:6: E0602: Undefined variable 'something_undefined' (undefined-variable) + """.format(module) ) - module = join(HERE, "regrtest_data", "application_crash.py") self._test_output([module, "-E"], expected_output=expected_output) def test_evaluation_score_shown_by_default(self): @@ -524,10 +524,10 @@ def test_pylintrc_comments_in_values(self): expected = textwrap.dedent( """ ************* Module test_pylintrc_comments - pylint/test/regrtest_data/test_pylintrc_comments.py:2:0: W0311: Bad indentation. Found 1 spaces, expected 4 (bad-indentation) - pylint/test/regrtest_data/test_pylintrc_comments.py:1:0: C0111: Missing module docstring (missing-docstring) - pylint/test/regrtest_data/test_pylintrc_comments.py:1:0: C0111: Missing function docstring (missing-docstring) - """ + {0}:2:0: W0311: Bad indentation. Found 1 spaces, expected 4 (bad-indentation) + {0}:1:0: C0111: Missing module docstring (missing-docstring) + {0}:1:0: C0111: Missing function docstring (missing-docstring) + """.format(path) ) self._test_output( [path, "--rcfile=%s" % config_path, "-rn"], expected_output=expected @@ -539,13 +539,13 @@ def test_no_crash_with_formatting_regex_defaults(self): ) def test_getdefaultencoding_crashes_with_lc_ctype_utf8(self): + module = join(HERE, "regrtest_data", "application_crash.py") expected_output = textwrap.dedent( """ ************* Module application_crash - pylint/test/regrtest_data/application_crash.py:1:6: E0602: Undefined variable 'something_undefined' (undefined-variable) - """ + {}:1:6: E0602: Undefined variable 'something_undefined' (undefined-variable) + """.format(module) ) - module = join(HERE, "regrtest_data", "application_crash.py") with _configure_lc_ctype("UTF-8"): self._test_output([module, "-E"], expected_output=expected_output) @@ -564,7 +564,7 @@ def test_parseable_file_path(self): self._test_output( [module, "--output-format=parseable"], - expected_output=join(os.getcwd(), file_name), + expected_output=file_name, ) finally: os.remove(module) @@ -573,7 +573,7 @@ def test_parseable_file_path(self): @pytest.mark.parametrize( "input_path,module,expected_path", [ - (join(HERE, "mymodule.py"), "mymodule", "pylint/test/mymodule.py"), + (join(HERE, "mymodule.py"), "mymodule", join(HERE, "mymodule.py")), ("mymodule.py", "mymodule", "mymodule.py"), ], ) diff --git a/pylint/test/unittest_checker_base.py b/tests/unittest_checker_base.py similarity index 100% rename from pylint/test/unittest_checker_base.py rename to tests/unittest_checker_base.py diff --git a/pylint/test/unittest_checker_classes.py b/tests/unittest_checker_classes.py similarity index 100% rename from pylint/test/unittest_checker_classes.py rename to tests/unittest_checker_classes.py diff --git a/pylint/test/unittest_checker_exceptions.py b/tests/unittest_checker_exceptions.py similarity index 100% rename from pylint/test/unittest_checker_exceptions.py rename to tests/unittest_checker_exceptions.py diff --git a/pylint/test/unittest_checker_format.py b/tests/unittest_checker_format.py similarity index 100% rename from pylint/test/unittest_checker_format.py rename to tests/unittest_checker_format.py diff --git a/pylint/test/unittest_checker_imports.py b/tests/unittest_checker_imports.py similarity index 100% rename from pylint/test/unittest_checker_imports.py rename to tests/unittest_checker_imports.py diff --git a/pylint/test/unittest_checker_logging.py b/tests/unittest_checker_logging.py similarity index 100% rename from pylint/test/unittest_checker_logging.py rename to tests/unittest_checker_logging.py diff --git a/pylint/test/unittest_checker_misc.py b/tests/unittest_checker_misc.py similarity index 100% rename from pylint/test/unittest_checker_misc.py rename to tests/unittest_checker_misc.py diff --git a/pylint/test/unittest_checker_python3.py b/tests/unittest_checker_python3.py similarity index 100% rename from pylint/test/unittest_checker_python3.py rename to tests/unittest_checker_python3.py diff --git a/pylint/test/unittest_checker_similar.py b/tests/unittest_checker_similar.py similarity index 100% rename from pylint/test/unittest_checker_similar.py rename to tests/unittest_checker_similar.py diff --git a/pylint/test/unittest_checker_spelling.py b/tests/unittest_checker_spelling.py similarity index 100% rename from pylint/test/unittest_checker_spelling.py rename to tests/unittest_checker_spelling.py diff --git a/pylint/test/unittest_checker_stdlib.py b/tests/unittest_checker_stdlib.py similarity index 100% rename from pylint/test/unittest_checker_stdlib.py rename to tests/unittest_checker_stdlib.py diff --git a/pylint/test/unittest_checker_strings.py b/tests/unittest_checker_strings.py similarity index 100% rename from pylint/test/unittest_checker_strings.py rename to tests/unittest_checker_strings.py diff --git a/pylint/test/unittest_checker_typecheck.py b/tests/unittest_checker_typecheck.py similarity index 100% rename from pylint/test/unittest_checker_typecheck.py rename to tests/unittest_checker_typecheck.py diff --git a/pylint/test/unittest_checker_variables.py b/tests/unittest_checker_variables.py similarity index 100% rename from pylint/test/unittest_checker_variables.py rename to tests/unittest_checker_variables.py diff --git a/pylint/test/unittest_checkers_utils.py b/tests/unittest_checkers_utils.py similarity index 100% rename from pylint/test/unittest_checkers_utils.py rename to tests/unittest_checkers_utils.py diff --git a/pylint/test/unittest_config.py b/tests/unittest_config.py similarity index 100% rename from pylint/test/unittest_config.py rename to tests/unittest_config.py diff --git a/pylint/test/unittest_lint.py b/tests/unittest_lint.py similarity index 100% rename from pylint/test/unittest_lint.py rename to tests/unittest_lint.py diff --git a/pylint/test/unittest_pyreverse_diadefs.py b/tests/unittest_pyreverse_diadefs.py similarity index 100% rename from pylint/test/unittest_pyreverse_diadefs.py rename to tests/unittest_pyreverse_diadefs.py diff --git a/pylint/test/unittest_pyreverse_inspector.py b/tests/unittest_pyreverse_inspector.py similarity index 98% rename from pylint/test/unittest_pyreverse_inspector.py rename to tests/unittest_pyreverse_inspector.py index fe5df9b981..d94638c3c5 100644 --- a/pylint/test/unittest_pyreverse_inspector.py +++ b/tests/unittest_pyreverse_inspector.py @@ -113,7 +113,7 @@ class Concrete23(Concrete1): pass def test_from_directory(project): - expected = os.path.join("pylint", "test", "data", "__init__.py") + expected = os.path.join("tests", "data", "__init__.py") assert project.name == "data" assert project.path.endswith(expected) diff --git a/pylint/test/unittest_pyreverse_writer.py b/tests/unittest_pyreverse_writer.py similarity index 100% rename from pylint/test/unittest_pyreverse_writer.py rename to tests/unittest_pyreverse_writer.py diff --git a/pylint/test/unittest_reporters_json.py b/tests/unittest_reporters_json.py similarity index 100% rename from pylint/test/unittest_reporters_json.py rename to tests/unittest_reporters_json.py diff --git a/pylint/test/unittest_reporting.py b/tests/unittest_reporting.py similarity index 100% rename from pylint/test/unittest_reporting.py rename to tests/unittest_reporting.py diff --git a/pylint/test/utils/unittest_ast_walker.py b/tests/utils/unittest_ast_walker.py similarity index 100% rename from pylint/test/utils/unittest_ast_walker.py rename to tests/utils/unittest_ast_walker.py diff --git a/pylint/test/utils/unittest_utils.py b/tests/utils/unittest_utils.py similarity index 100% rename from pylint/test/utils/unittest_utils.py rename to tests/utils/unittest_utils.py diff --git a/tox.ini b/tox.ini index 5d58ddde28..b4864e679b 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,7 @@ deps = black==19.3b0 isort==4.3.15 commands = - black --check --exclude "functional|input|test/extension|test/regrtest_data|test/data" pylint + black --check pylint isort -rc pylint/ --check-only changedir = {toxinidir} @@ -41,7 +41,7 @@ setenv = COVERAGE_FILE = {toxinidir}/.coverage.{envname} commands = - python -Wi {envsitepackagesdir}/coverage run -m pytest {envsitepackagesdir}/pylint/test/ {posargs:} + python -Wi {envsitepackagesdir}/coverage run -m pytest {toxinidir}/tests/ {posargs:} ; Transform absolute path to relative path ; for compatibility with coveralls.io and fix 'source not available' error. @@ -58,7 +58,7 @@ deps = pyenchant commands = - python -Wi -m pytest {envsitepackagesdir}/pylint/test/ {posargs:} -k unittest_checker_spelling + python -Wi -m pytest {toxinidir}/tests/ {posargs:} -k unittest_checker_spelling changedir = {toxworkdir} From 389b9d24a0362227832162ffada60abed25a0e28 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 20 Jun 2019 10:04:40 +0200 Subject: [PATCH 0138/5147] Bring back Travis stages --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a9eedab1b6..7a7ed40a25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,8 @@ jobs: env: TOXENV=formatting - python: 3.6 env: TOXENV=mypy - - python: 3.5-dev + - stage: tests-cpython + python: 3.5-dev env: TOXENV=py35 - python: 3.6 env: TOXENV=py36 From ee228ddacd003ea40b1eeed64fda5fe96fce4c7c Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 20 Jun 2019 11:11:31 +0200 Subject: [PATCH 0139/5147] Normalize paths to get the self tests working on both Linux and Windows --- tests/test_self.py | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/tests/test_self.py b/tests/test_self.py index 051eea87d7..3cf11ad6b3 100644 --- a/tests/test_self.py +++ b/tests/test_self.py @@ -34,12 +34,12 @@ import pytest -from pylint import utils from pylint.lint import Run -from pylint.reporters import BaseReporter, JSONReporter +from pylint.reporters import JSONReporter from pylint.reporters.text import * HERE = abspath(dirname(__file__)) +CLEAN_PATH = dirname(dirname(__file__)) + "/" @contextlib.contextmanager @@ -121,16 +121,16 @@ def _run_pylint(self, args, out, reporter=None): Run(args, reporter=reporter) return cm.value.code - def _clean_paths(self, output): - """Remove version-specific tox parent directories from paths.""" - return re.sub( - "^py.+/site-packages/", "", output.replace("\\", "/"), flags=re.MULTILINE - ) + @staticmethod + def _clean_paths(output): + """Normalize path to the tests directory.""" + return re.sub(CLEAN_PATH, "", output.replace("\\", "/"), flags=re.MULTILINE) def _test_output(self, args, expected_output): out = StringIO() self._run_pylint(args, out=out) actual_output = self._clean_paths(out.getvalue()) + expected_output = self._clean_paths(expected_output) assert expected_output.strip() in actual_output.strip() def test_pkginfo(self): @@ -342,7 +342,9 @@ def test_enable_all_works(self): {0}:10:8: W0612: Unused variable 'local_variable' (unused-variable) {0}:18:4: C0111: Missing method docstring (missing-docstring) {0}:22:0: C0111: Missing class docstring (missing-docstring) - """.format(module) + """.format( + module + ) ) self._test_output( [module, "--disable=all", "--enable=all", "-rn"], expected_output=expected @@ -355,7 +357,9 @@ def test_wrong_import_position_when_others_disabled(self): """ ************* Module wrong_import_position {}:11:0: C0413: Import "import os" should be placed at the top of the module (wrong-import-position) - """.format(module2) + """.format( + module2 + ) ) args = [ module2, @@ -376,7 +380,7 @@ def test_wrong_import_position_when_others_disabled(self): # If ~/.pylintrc is present remove the # Using config file... line actual_output = actual_output[actual_output.find("\n") :] - assert expected_output.strip() == actual_output.strip() + assert self._clean_paths(expected_output.strip()) == actual_output.strip() def test_import_itself_not_accounted_for_relative_imports(self): expected = "Your code has been rated at 10.00/10" @@ -467,7 +471,9 @@ def test_error_mode_shows_no_score(self): """ ************* Module application_crash {}:1:6: E0602: Undefined variable 'something_undefined' (undefined-variable) - """.format(module) + """.format( + module + ) ) self._test_output([module, "-E"], expected_output=expected_output) @@ -527,7 +533,9 @@ def test_pylintrc_comments_in_values(self): {0}:2:0: W0311: Bad indentation. Found 1 spaces, expected 4 (bad-indentation) {0}:1:0: C0111: Missing module docstring (missing-docstring) {0}:1:0: C0111: Missing function docstring (missing-docstring) - """.format(path) + """.format( + path + ) ) self._test_output( [path, "--rcfile=%s" % config_path, "-rn"], expected_output=expected @@ -542,9 +550,10 @@ def test_getdefaultencoding_crashes_with_lc_ctype_utf8(self): module = join(HERE, "regrtest_data", "application_crash.py") expected_output = textwrap.dedent( """ - ************* Module application_crash {}:1:6: E0602: Undefined variable 'something_undefined' (undefined-variable) - """.format(module) + """.format( + module + ) ) with _configure_lc_ctype("UTF-8"): self._test_output([module, "-E"], expected_output=expected_output) @@ -563,8 +572,7 @@ def test_parseable_file_path(self): test_target.write("a,b = object()") self._test_output( - [module, "--output-format=parseable"], - expected_output=file_name, + [module, "--output-format=parseable"], expected_output=file_name ) finally: os.remove(module) From fd5c80efbffa5daa555aa90cd0b937059a09a2da Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 13 Apr 2019 21:25:19 +0200 Subject: [PATCH 0140/5147] Test - Add unittest for MessageStore format help --- tests/message/unittest_message_store.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/message/unittest_message_store.py b/tests/message/unittest_message_store.py index 053bfca500..1012894d8d 100644 --- a/tests/message/unittest_message_store.py +++ b/tests/message/unittest_message_store.py @@ -38,6 +38,31 @@ class Checker(BaseChecker): return store +def test_format_help(capsys, store): + store.help_message([]) + captured = capsys.readouterr() + assert captured.out == "" + store.help_message(["W1234", "E1234"]) + captured = capsys.readouterr() + assert ( + captured.out + == """:msg-symbol (W1234): *message* + msg description. This message belongs to the achecker checker. + +:duplicate-keyword-arg (E1234): *Duplicate keyword argument %r in %s call* + Used when a function call passes the same keyword argument multiple times. + This message belongs to the achecker checker. It can't be emitted when using + Python >= 2.6. + +""" + ) + + +def test_get_msg_display_string(store): + assert store.get_msg_display_string("W1234") == "'msg-symbol'" + assert store.get_msg_display_string("E1234") == "'duplicate-keyword-arg'" + + class TestMessagesStore(object): def _compare_messages(self, desc, msg, checkerref=False): assert desc == msg.format_help(checkerref=checkerref) From 0a1ae3f9df31e1fb3117eb3e796dd10c4cab3091 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 13 Apr 2019 21:57:04 +0200 Subject: [PATCH 0141/5147] Test - Handle the case where the msgid does not exist --- tests/message/unittest_message_store.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/message/unittest_message_store.py b/tests/message/unittest_message_store.py index 1012894d8d..732cb7872a 100644 --- a/tests/message/unittest_message_store.py +++ b/tests/message/unittest_message_store.py @@ -42,7 +42,7 @@ def test_format_help(capsys, store): store.help_message([]) captured = capsys.readouterr() assert captured.out == "" - store.help_message(["W1234", "E1234"]) + store.help_message(["W1234", "E1234", "C1234"]) captured = capsys.readouterr() assert ( captured.out @@ -54,6 +54,8 @@ def test_format_help(capsys, store): This message belongs to the achecker checker. It can't be emitted when using Python >= 2.6. +No such message id or symbol 'C1234'. + """ ) From 56965fe272a867fd5cd915aef52239a3d566bb83 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Mar 2019 14:05:50 +0100 Subject: [PATCH 0142/5147] Style - Less verbose print with triple quote strings --- pylint/message/message_handler_mix_in.py | 41 ++++++++++++++---------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 53eb86870a..9013b6856a 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -349,12 +349,15 @@ def print_full_documentation(self, stream=None): """output a full documentation in ReST format""" if not stream: stream = sys.stdout - - print("Pylint global options and switches", file=stream) - print("----------------------------------", file=stream) - print("", file=stream) - print("Pylint provides global options and switches.", file=stream) - print("", file=stream) + print( + """\ +Pylint global options and switches +---------------------------------- + +Pylint provides global options and switches. +""", + file=stream, + ) by_checker = {} for checker in self.get_checkers(): @@ -382,17 +385,21 @@ def print_full_documentation(self, stream=None): "reports": list(checker.reports), } - print("Pylint checkers' options and switches", file=stream) - print("-------------------------------------", file=stream) - print("", file=stream) - print("Pylint checkers can provide three set of features:", file=stream) - print("", file=stream) - print("* options that control their execution,", file=stream) - print("* messages that they can raise,", file=stream) - print("* reports that they can generate.", file=stream) - print("", file=stream) - print("Below is a list of all checkers and their features.", file=stream) - print("", file=stream) + print( + """\ +Pylint checkers' options and switches +------------------------------------- + +Pylint checkers can provide three set of features: + +* options that control their execution, +* messages that they can raise, +* reports that they can generate. + +Below is a list of all checkers and their features. +""", + file=stream, + ) for checker, info in sorted(by_checker.items()): self._print_checker_doc(checker, info, stream=stream) From 2b43736654e1f52487c9edfd794c39782e64d7e9 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Mar 2019 18:21:45 +0100 Subject: [PATCH 0143/5147] Feat - Add a __repr__ and __gt__ function for BaseCheckers Permit pylint's developper to see what is inside a sorted list of checkers. --- pylint/checkers/base_checker.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index 6ce8ecf9f1..55d2737f8b 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -43,6 +43,17 @@ def __init__(self, linter=None): 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" + msgids = [id for id in self.msgs] + return "{} '{}' responsible for {}".format(status, self.name, ", ".join(msgids)) + def add_message( self, msgid, From cdd4d4bbe29b17b58b460d50bf46ced1b088e34e Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 9 Jun 2019 18:20:21 +0200 Subject: [PATCH 0144/5147] Feat - Permit to get message definition by msgid in Checkers --- pylint/checkers/base_checker.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index 55d2737f8b..cb177ef647 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -98,6 +98,14 @@ def messages(self) -> list: # 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)""" From 75e28a196b7025e1950c1ddd5437f6cdd837d098 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 9 Jun 2019 22:39:48 +0200 Subject: [PATCH 0145/5147] Refactor - Put a checker instance in informations --- pylint/message/message_handler_mix_in.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 9013b6856a..a19284305a 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -375,11 +375,13 @@ def print_full_documentation(self, stream=None): else: name = checker.name try: + by_checker[name]["checker"] = checker by_checker[name]["options"] += checker.options_and_values() by_checker[name]["msgs"].update(checker.msgs) by_checker[name]["reports"] += checker.reports except KeyError: by_checker[name] = { + "checker": checker, "options": list(checker.options_and_values()), "msgs": dict(checker.msgs), "reports": list(checker.reports), @@ -412,14 +414,14 @@ def _print_checker_doc(checker_name, info, stream=None): """ if not stream: stream = sys.stdout - + checker = info.get("checker") doc = info.get("doc") module = info.get("module") msgs = info.get("msgs") options = info.get("options") reports = info.get("reports") - checker_title = "%s checker" % (checker_name.replace("_", " ").title()) + checker_title = "%s checker" % (checker.name.replace("_", " ").title()) if module: # Provide anchor to link against @@ -429,7 +431,7 @@ def _print_checker_doc(checker_name, info, stream=None): print("", file=stream) if module: print("This checker is provided by ``%s``." % module, file=stream) - print("Verbatim name of the checker is ``%s``." % checker_name, file=stream) + print("Verbatim name of the checker is ``%s``." % checker.name, file=stream) print("", file=stream) if doc: # Provide anchor to link against @@ -451,7 +453,7 @@ def _print_checker_doc(checker_name, info, stream=None): for msgid, msg in sorted( msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) ): - msg = build_message_definition(checker_name, msgid, msg) + msg = build_message_definition(checker.name, msgid, msg) print(msg.format_help(checkerref=False), file=stream) print("", file=stream) if reports: From b398aafa99c144d60c7f5498ef3e150c0e12ec7e Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 9 Jun 2019 23:01:01 +0200 Subject: [PATCH 0146/5147] Refactor - Give a checker instead of a string to _print_checker_doc Will permit to move functions in the BaseChecker class later. --- doc/exts/pylint_extensions.py | 30 +++++++++++------------- pylint/message/message_handler_mix_in.py | 8 +++---- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/doc/exts/pylint_extensions.py b/doc/exts/pylint_extensions.py index dd660767c9..016ba6f54e 100755 --- a/doc/exts/pylint_extensions.py +++ b/doc/exts/pylint_extensions.py @@ -65,14 +65,13 @@ def builder_inited(app): "\n load-plugins=pylint.extensions.docparams," "pylint.extensions.docstyle\n\n" ) - by_module = get_plugins_info(linter, doc_files) - for module, info in sorted(by_module.items()): - linter._print_checker_doc(info["name"], info, stream=stream) + by_checker = get_plugins_info(linter, doc_files) + for checker, info in sorted(by_checker.items()): + linter._print_checker_doc(checker, info, stream=stream) def get_plugins_info(linter, doc_files): - by_module = {} - + by_checker = {} for checker in linter.get_checkers(): if checker.name == "master": continue @@ -80,23 +79,23 @@ def get_plugins_info(linter, doc_files): # Plugins only - skip over core checkers if re.match("pylint.checkers", module): continue - # Find any .rst documentation associated with this plugin doc = "" doc_file = doc_files.get(module) if doc_file: with open(doc_file, "r") as f: doc = f.read() - try: - by_module[module]["options"] += checker.options_and_values() - by_module[module]["msgs"].update(checker.msgs) - by_module[module]["reports"] += checker.reports - by_module[module]["doc"] += doc - by_module[module]["name"] += checker.name - by_module[module]["module"] += module + by_checker[checker]["checker"] = checker + by_checker[checker]["options"] += checker.options_and_values() + by_checker[checker]["msgs"].update(checker.msgs) + by_checker[checker]["reports"] += checker.reports + by_checker[checker]["doc"] += doc + by_checker[checker]["name"] += checker.name + by_checker[checker]["module"] += module except KeyError: - by_module[module] = { + by_checker[checker] = { + "checker": checker, "options": list(checker.options_and_values()), "msgs": dict(checker.msgs), "reports": list(checker.reports), @@ -104,8 +103,7 @@ def get_plugins_info(linter, doc_files): "name": checker.name, "module": module, } - - return by_module + return by_checker def setup(app): diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index a19284305a..f007e8edef 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -361,6 +361,7 @@ def print_full_documentation(self, stream=None): by_checker = {} for checker in self.get_checkers(): + name = checker.name if checker.name == "master": if checker.options: for section, options in checker.options_by_section(): @@ -373,7 +374,6 @@ def print_full_documentation(self, stream=None): _rest_format_section(stream, None, options) print("", file=stream) else: - name = checker.name try: by_checker[name]["checker"] = checker by_checker[name]["options"] += checker.options_and_values() @@ -403,11 +403,11 @@ def print_full_documentation(self, stream=None): file=stream, ) - for checker, info in sorted(by_checker.items()): - self._print_checker_doc(checker, info, stream=stream) + for checker, information in sorted(by_checker.items()): + self._print_checker_doc(checker, information, stream=stream) @staticmethod - def _print_checker_doc(checker_name, info, stream=None): + def _print_checker_doc(checker, info, stream=None): """Helper method for print_full_documentation. Also used by doc/exts/pylint_extensions.py. From b2ccc588997354e13216705143aed8d615360ee1 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 9 Jun 2019 23:02:34 +0200 Subject: [PATCH 0147/5147] Fix - better error msg for getmessagedefinition (sqsuash) --- pylint/checkers/base_checker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index cb177ef647..5c3d529099 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -102,8 +102,8 @@ 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]) + 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): From 275b9eba23ed7b839a94f432dda608ffa66eed51 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Mar 2019 14:51:41 +0100 Subject: [PATCH 0148/5147] Refactor - Separate string creation and display in rest_format_section This will permit to get the string without printing it at the same time. Print add an implicit \n so if this look convuluted remember that. We had to change step by step in order to be able to not have too much complexity at once. --- pylint/message/message_handler_mix_in.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index f007e8edef..3e36edaac8 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -30,23 +30,24 @@ ) -def _rest_format_section(stream, section, options, doc=None): +def _rest_format_section(section, options, doc=None) -> str: """format an options section using as ReST formatted output""" + result = "" if section: - print("%s\n%s" % (section, "'" * len(section)), file=stream) + result += "%s\n%s\n" % (section, "'" * len(section)) if doc: - print(normalize_text(doc, line_len=79, indent=""), file=stream) - print(file=stream) + formatted_doc = normalize_text(doc, line_len=79, indent="") + result += "%s\n\n" % formatted_doc for optname, optdict, value in options: help_opt = optdict.get("help") - print(":%s:" % optname, file=stream) + result += ":%s:\n" % optname if help_opt: - help_opt = normalize_text(help_opt, line_len=79, indent=" ") - print(help_opt, file=stream) + formatted_help = normalize_text(help_opt, line_len=79, indent=" ") + result += "%s\n" % formatted_help if value: value = str(_format_option_value(optdict, value)) - print(file=stream) - print(" Default: ``%s``" % value.replace("`` ", "```` ``"), file=stream) + result += "\n Default: ``%s``\n" % value.replace("`` ", "```` ``") + return result class MessagesHandlerMixIn: @@ -371,7 +372,7 @@ def print_full_documentation(self, stream=None): title = "%s options" % section.capitalize() print(title, file=stream) print("~" * len(title), file=stream) - _rest_format_section(stream, None, options) + print(_rest_format_section(None, options)[:-1], file=stream) print("", file=stream) else: try: @@ -444,7 +445,7 @@ def _print_checker_doc(checker, info, stream=None): title = "{} Options".format(checker_title) print(title, file=stream) print("^" * len(title), file=stream) - _rest_format_section(stream, None, options) + print(_rest_format_section(None, options)[:-1], file=stream) print("", file=stream) if msgs: title = "{} Messages".format(checker_title) From ff07497ef6bd9cd02929246b4ad1d944b6de9919 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 9 Jun 2019 23:37:16 +0200 Subject: [PATCH 0149/5147] Refactor - Separate string creation and display in _print_checker_doc This will permit to get the string without printing it at the same time. This will also permit to move this to BaseChecker. --- pylint/message/message_handler_mix_in.py | 109 +++++++++++------------ 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 3e36edaac8..ccbd7cf8c4 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -30,11 +30,16 @@ ) +def get_rest_title(title, character): + """Permit to get a rest title underlined with a choosen character.""" + return "%s\n%s\n" % (title, character * len(title)) + + def _rest_format_section(section, options, doc=None) -> str: """format an options section using as ReST formatted output""" result = "" if section: - result += "%s\n%s\n" % (section, "'" * len(section)) + result += get_rest_title(section, "'") if doc: formatted_doc = normalize_text(doc, line_len=79, indent="") result += "%s\n\n" % formatted_doc @@ -346,6 +351,25 @@ def add_one_message( ) ) + def _get_checkers_infos(self) -> dict: + by_checker = {} + for checker in self.get_checkers(): + name = checker.name + if name != "master": + try: + by_checker[name]["checker"] = checker + by_checker[name]["options"] += checker.options_and_values() + by_checker[name]["msgs"].update(checker.msgs) + by_checker[name]["reports"] += checker.reports + except KeyError: + by_checker[name] = { + "checker": checker, + "options": list(checker.options_and_values()), + "msgs": dict(checker.msgs), + "reports": list(checker.reports), + } + return by_checker + def print_full_documentation(self, stream=None): """output a full documentation in ReST format""" if not stream: @@ -359,8 +383,6 @@ def print_full_documentation(self, stream=None): """, file=stream, ) - - by_checker = {} for checker in self.get_checkers(): name = checker.name if checker.name == "master": @@ -374,20 +396,6 @@ def print_full_documentation(self, stream=None): print("~" * len(title), file=stream) print(_rest_format_section(None, options)[:-1], file=stream) print("", file=stream) - else: - try: - by_checker[name]["checker"] = checker - by_checker[name]["options"] += checker.options_and_values() - by_checker[name]["msgs"].update(checker.msgs) - by_checker[name]["reports"] += checker.reports - except KeyError: - by_checker[name] = { - "checker": checker, - "options": list(checker.options_and_values()), - "msgs": dict(checker.msgs), - "reports": list(checker.reports), - } - print( """\ Pylint checkers' options and switches @@ -403,65 +411,56 @@ def print_full_documentation(self, stream=None): """, file=stream, ) - + by_checker = self._get_checkers_infos() for checker, information in sorted(by_checker.items()): self._print_checker_doc(checker, information, stream=stream) @staticmethod - def _print_checker_doc(checker, info, stream=None): - """Helper method for print_full_documentation. - - Also used by doc/exts/pylint_extensions.py. - """ - if not stream: - stream = sys.stdout + def _get_checker_doc(checker, info) -> str: + result = "" checker = info.get("checker") doc = info.get("doc") module = info.get("module") msgs = info.get("msgs") options = info.get("options") reports = info.get("reports") - checker_title = "%s checker" % (checker.name.replace("_", " ").title()) - if module: # Provide anchor to link against - print(".. _%s:\n" % module, file=stream) - print(checker_title, file=stream) - print("~" * len(checker_title), file=stream) - print("", file=stream) + result += ".. _%s:\n\n" % module + result += "%s\n" % get_rest_title(checker_title, "~") if module: - print("This checker is provided by ``%s``." % module, file=stream) - print("Verbatim name of the checker is ``%s``." % checker.name, file=stream) - print("", file=stream) + result += "This checker is provided by ``%s``.\n" % module + result += "Verbatim name of the checker is ``%s``.\n\n" % checker.name if doc: # Provide anchor to link against - title = "{} Documentation".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) - print(cleandoc(doc), file=stream) - print("", file=stream) + result += get_rest_title("{} Documentation".format(checker_title), "^") + result += "%s\n\n" % cleandoc(doc) if options: - title = "{} Options".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) - print(_rest_format_section(None, options)[:-1], file=stream) - print("", file=stream) + result += get_rest_title("{} Options".format(checker_title), "^") + result += "%s\n" % _rest_format_section(None, options) if msgs: - title = "{} Messages".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) + result += get_rest_title("{} Messages".format(checker_title), "^") for msgid, msg in sorted( msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) ): msg = build_message_definition(checker.name, msgid, msg) - print(msg.format_help(checkerref=False), file=stream) - print("", file=stream) + result += "%s\n" % msg.format_help(checkerref=False) + result += "\n" if reports: - title = "{} Reports".format(checker_title) - print(title, file=stream) - print("^" * len(title), file=stream) + result += get_rest_title("{} Reports".format(checker_title), "^") for report in reports: - print(":%s: %s" % report[:2], file=stream) - print("", file=stream) - print("", file=stream) + result += ":%s: %s\n" % report[:2] + result += "\n" + result += "\n" + return result + + @staticmethod + def _print_checker_doc(checker, info, stream=None): + """Helper method for print_full_documentation. + + Also used by doc/exts/pylint_extensions.py. + """ + if not stream: + stream = sys.stdout + print(MessagesHandlerMixIn._get_checker_doc(checker, info)[:-1], file=stream) From c12afa279e7749def33371db7b162f26bf5e78d9 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 24 Mar 2019 17:21:10 +0100 Subject: [PATCH 0150/5147] Fix - Add type annotation for mypy --- pylint/message/message_handler_mix_in.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index ccbd7cf8c4..d3a3da5176 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -352,7 +352,7 @@ def add_one_message( ) def _get_checkers_infos(self) -> dict: - by_checker = {} + by_checker: dict = {} for checker in self.get_checkers(): name = checker.name if name != "master": From 66fcf4511f3d02701b67edd4674fa19e08833960 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Mar 2019 18:09:35 +0100 Subject: [PATCH 0151/5147] Refactor - Separate string creation and display in _print_full_documentation This will permit to get the string without printing it at the same time. --- pylint/message/message_handler_mix_in.py | 36 +++++++++++------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index d3a3da5176..942b5ec4be 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -370,19 +370,14 @@ def _get_checkers_infos(self) -> dict: } return by_checker - def print_full_documentation(self, stream=None): - """output a full documentation in ReST format""" - if not stream: - stream = sys.stdout - print( - """\ + def get_full_documentation(self) -> str: + result = """\ Pylint global options and switches ---------------------------------- Pylint provides global options and switches. -""", - file=stream, - ) + +""" for checker in self.get_checkers(): name = checker.name if checker.name == "master": @@ -392,12 +387,9 @@ def print_full_documentation(self, stream=None): title = "General options" else: title = "%s options" % section.capitalize() - print(title, file=stream) - print("~" * len(title), file=stream) - print(_rest_format_section(None, options)[:-1], file=stream) - print("", file=stream) - print( - """\ + result += get_rest_title(title, "~") + result += "%s\n" % _rest_format_section(None, options) + result += """\ Pylint checkers' options and switches ------------------------------------- @@ -408,12 +400,18 @@ def print_full_documentation(self, stream=None): * reports that they can generate. Below is a list of all checkers and their features. -""", - file=stream, - ) + +""" by_checker = self._get_checkers_infos() for checker, information in sorted(by_checker.items()): - self._print_checker_doc(checker, information, stream=stream) + result += self._get_checker_doc(checker, information) + return result + + def print_full_documentation(self, stream=None) -> None: + """output a full documentation in ReST format""" + if not stream: + stream = sys.stdout + print(self.get_full_documentation()[:-1], file=stream) @staticmethod def _get_checker_doc(checker, info) -> str: From e96be13c47b735ecc78f2e400568f2af2cbaccb1 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 23 Mar 2019 10:48:20 +0100 Subject: [PATCH 0152/5147] Refactor - Move build_message_definition to the BaseChecker class --- pylint/checkers/base_checker.py | 41 ++++++++++++++++------ pylint/message/__init__.py | 1 - pylint/message/build_message_definition.py | 34 ------------------ pylint/message/message_handler_mix_in.py | 3 +- 4 files changed, 32 insertions(+), 47 deletions(-) delete mode 100644 pylint/message/build_message_definition.py diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index 5c3d529099..809694398d 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -14,9 +14,10 @@ from typing import Any from pylint.config import OptionsProviderMixIn +from pylint.constants import WarningScope from pylint.exceptions import InvalidMessageError -from pylint.interfaces import UNDEFINED -from pylint.message import build_message_definition +from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements +from pylint.message.message_definition import MessageDefinition class BaseChecker(OptionsProviderMixIn): @@ -55,14 +56,10 @@ def __repr__(self): return "{} '{}' responsible for {}".format(status, self.name, ", ".join(msgids)) def add_message( - self, - msgid, - line=None, - node=None, - args=None, - confidence=UNDEFINED, - col_offset=None, + 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) -> None: @@ -89,10 +86,34 @@ def check_consistency(self) -> None: 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 [ - build_message_definition(self, msgid, msg_tuple) + self.create_message_definition_from_tuple(msgid, msg_tuple) for msgid, msg_tuple in sorted(self.msgs.items()) ] diff --git a/pylint/message/__init__.py b/pylint/message/__init__.py index 430d066539..1616eeea0c 100644 --- a/pylint/message/__init__.py +++ b/pylint/message/__init__.py @@ -39,7 +39,6 @@ """All the classes related to Message handling.""" -from pylint.message.build_message_definition import build_message_definition from pylint.message.message import Message from pylint.message.message_definition import MessageDefinition from pylint.message.message_handler_mix_in import MessagesHandlerMixIn diff --git a/pylint/message/build_message_definition.py b/pylint/message/build_message_definition.py deleted file mode 100644 index 4de204d048..0000000000 --- a/pylint/message/build_message_definition.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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 - -import warnings - -from pylint.constants import WarningScope -from pylint.interfaces import IRawChecker, ITokenChecker, implements -from pylint.message.message_definition import MessageDefinition - - -def build_message_definition(checker, msgid, msg_tuple): - if implements(checker, (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: - # messages should have a symbol, but for backward compatibility - # they may not. - (msg, descr) = msg_tuple - warnings.warn( - "[pylint 0.26] description of message %s doesn't include " - "a symbolic name" % msgid, - DeprecationWarning, - ) - symbol = None - options.setdefault("scope", default_scope) - return MessageDefinition(checker, msgid, msg, descr, symbol, **options) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 942b5ec4be..388054897b 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -20,7 +20,6 @@ ) from pylint.exceptions import InvalidMessageError, UnknownMessageError from pylint.interfaces import UNDEFINED -from pylint.message.build_message_definition import build_message_definition from pylint.message.message import Message from pylint.utils.utils import ( _format_option_value, @@ -442,7 +441,7 @@ def _get_checker_doc(checker, info) -> str: for msgid, msg in sorted( msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) ): - msg = build_message_definition(checker.name, msgid, msg) + msg = checker.create_message_definition_from_tuple(msgid, msg) result += "%s\n" % msg.format_help(checkerref=False) result += "\n" if reports: From 8a09f2dda7669e1454de00700dd1ce6419a7ab52 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 19 May 2019 12:10:25 +0200 Subject: [PATCH 0153/5147] Refactor - Use a constant for the main checker name Following review see : https://github.com/PyCQA/pylint/pull/2844#discussion_r281014968 --- doc/exts/pylint_extensions.py | 3 ++- pylint/constants.py | 2 ++ pylint/lint.py | 10 +++++++--- pylint/message/message_handler_mix_in.py | 3 ++- tests/test_self.py | 5 ++++- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/doc/exts/pylint_extensions.py b/doc/exts/pylint_extensions.py index 016ba6f54e..6f2eaa5c47 100755 --- a/doc/exts/pylint_extensions.py +++ b/doc/exts/pylint_extensions.py @@ -11,6 +11,7 @@ import pkg_resources import sphinx +from pylint.constants import MAIN_CHECKER_NAME from pylint.lint import PyLinter # Some modules have been renamed and deprecated under their old names. @@ -73,7 +74,7 @@ def builder_inited(app): def get_plugins_info(linter, doc_files): by_checker = {} for checker in linter.get_checkers(): - if checker.name == "master": + if checker.name == MAIN_CHECKER_NAME: continue module = checker.__module__ # Plugins only - skip over core checkers diff --git a/pylint/constants.py b/pylint/constants.py index 07d1727ab2..a2e2b79777 100644 --- a/pylint/constants.py +++ b/pylint/constants.py @@ -32,6 +32,8 @@ MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1} +MAIN_CHECKER_NAME = "main-pylint-checker" + class WarningScope: LINE = "line-based-msg" diff --git a/pylint/lint.py b/pylint/lint.py index f599196490..a19865343d 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -77,7 +77,7 @@ from pylint import checkers, config, exceptions, interfaces, reporters from pylint.__pkginfo__ import version -from pylint.constants import MSG_TYPES, OPTION_RGX +from pylint.constants import MAIN_CHECKER_NAME, MSG_TYPES, OPTION_RGX from pylint.message import Message, MessagesHandlerMixIn, MessagesStore from pylint.reporters.ureports import nodes as report_nodes from pylint.utils import ASTWalker, FileState, utils @@ -326,7 +326,7 @@ class PyLinter( __implements__ = (interfaces.ITokenChecker,) - name = "master" + name = MAIN_CHECKER_NAME priority = 0 level = 0 msgs = MSGS @@ -922,7 +922,11 @@ def get_checker_names(self): """Get all the checker names that this linter knows about.""" current_checkers = self.get_checkers() return sorted( - {check.name for check in current_checkers if check.name != "master"} + { + checker.name + for checker in current_checkers + if checker.name != MAIN_CHECKER_NAME + } ) def prepare_checkers(self): diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 388054897b..f34ba5a4cc 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -11,6 +11,7 @@ from pylint.constants import ( _MSG_ORDER, _SCOPE_EXEMPT, + MAIN_CHECKER_NAME, MSG_STATE_CONFIDENCE, MSG_STATE_SCOPE_CONFIG, MSG_STATE_SCOPE_MODULE, @@ -379,7 +380,7 @@ def get_full_documentation(self) -> str: """ for checker in self.get_checkers(): name = checker.name - if checker.name == "master": + if name == MAIN_CHECKER_NAME: if checker.options: for section, options in checker.options_by_section(): if section is None: diff --git a/tests/test_self.py b/tests/test_self.py index 3cf11ad6b3..a1e3bb4465 100644 --- a/tests/test_self.py +++ b/tests/test_self.py @@ -34,6 +34,7 @@ import pytest +from pylint.constants import MAIN_CHECKER_NAME from pylint.lint import Run from pylint.reporters import JSONReporter from pylint.reporters.text import * @@ -183,7 +184,9 @@ def test_generate_config_disable_symbolic_names(self): output = out.getvalue() # Get rid of the pesky messages that pylint emits if the # configuration file is not found. - master = re.search(r"\[MASTER", output) + pattern = r"\[{}".format(MAIN_CHECKER_NAME.upper()) + master = re.search(pattern, output) + assert master is not None, "{} not found in {}".format(pattern, output) out = StringIO(output[master.start() :]) parser = configparser.RawConfigParser() parser.read_file(out) From 748bb0b60f10b3667bf803b60a98e7a6830a9767 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 19 May 2019 12:44:42 +0200 Subject: [PATCH 0154/5147] Doc - Retrocompatibility with the main checker name --- pylint/constants.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pylint/constants.py b/pylint/constants.py index a2e2b79777..852fc15108 100644 --- a/pylint/constants.py +++ b/pylint/constants.py @@ -32,7 +32,10 @@ MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1} -MAIN_CHECKER_NAME = "main-pylint-checker" +# You probably don't want to change the MAIN_CHECKER_NAME +# This would affect rcfile generation and retro-compatibility +# on all project using [MASTER] in their rcfile. +MAIN_CHECKER_NAME = "master" class WarningScope: From 75b68a4123cb417a1c4e8ca989011e02194a6b01 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 19 May 2019 13:29:59 +0200 Subject: [PATCH 0155/5147] Refactor - Simplify the Checker.__repr__ function Following review see : https://github.com/PyCQA/pylint/pull/2844#discussion_r281015055 --- pylint/checkers/base_checker.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index 809694398d..1aa83d6d9e 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -52,8 +52,9 @@ def __gt__(self, other): def __repr__(self): status = "Checker" if self.enabled else "Disabled checker" - msgids = [id for id in self.msgs] - return "{} '{}' responsible for {}".format(status, self.name, ", ".join(msgids)) + return "{} '{}' responsible for {}".format( + status, self.name, ", ".join(self.msgs.keys()) + ) def add_message( self, msgid, line=None, node=None, args=None, confidence=None, col_offset=None From 9e9601f2330b9f5f7fb58f8ee4e3e5955f269e3e Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 9 Jun 2019 17:14:10 +0200 Subject: [PATCH 0156/5147] Fix - A typo in base_checker.py --- pylint/checkers/base_checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index 1aa83d6d9e..d4f38886ed 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -26,7 +26,7 @@ class BaseChecker(OptionsProviderMixIn): 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 ckecker behaviour + # ordered list of options to control the checker behaviour options = () # type: Any # messages issued by this checker msgs = {} # type: Any From 17c468e82d88560c7155471bf0a613605fbe3cd4 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 10 Jun 2019 08:29:09 +0200 Subject: [PATCH 0157/5147] Refactor - Remove syntax specific to python 3.6 Following review see here : https://github.com/PyCQA/pylint/pull/2844#discussion_r281014957 --- pylint/message/message_handler_mix_in.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index f34ba5a4cc..2dd863513e 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -35,7 +35,7 @@ def get_rest_title(title, character): return "%s\n%s\n" % (title, character * len(title)) -def _rest_format_section(section, options, doc=None) -> str: +def _rest_format_section(section, options, doc=None): """format an options section using as ReST formatted output""" result = "" if section: @@ -351,7 +351,7 @@ def add_one_message( ) ) - def _get_checkers_infos(self) -> dict: + def _get_checkers_infos(self): by_checker: dict = {} for checker in self.get_checkers(): name = checker.name @@ -370,7 +370,7 @@ def _get_checkers_infos(self) -> dict: } return by_checker - def get_full_documentation(self) -> str: + def get_full_documentation(self): result = """\ Pylint global options and switches ---------------------------------- @@ -407,14 +407,14 @@ def get_full_documentation(self) -> str: result += self._get_checker_doc(checker, information) return result - def print_full_documentation(self, stream=None) -> None: + def print_full_documentation(self, stream=None): """output a full documentation in ReST format""" if not stream: stream = sys.stdout print(self.get_full_documentation()[:-1], file=stream) @staticmethod - def _get_checker_doc(checker, info) -> str: + def _get_checker_doc(checker, info): result = "" checker = info.get("checker") doc = info.get("doc") From 2cabf33c96008b1f27d591a1495c3720869de4c9 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 10 Jun 2019 08:33:18 +0200 Subject: [PATCH 0158/5147] Refactor - Remove _ in _rest_format_section Because we want to make it a public function. --- pylint/message/message_handler_mix_in.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 2dd863513e..7676a7f507 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -35,7 +35,7 @@ def get_rest_title(title, character): return "%s\n%s\n" % (title, character * len(title)) -def _rest_format_section(section, options, doc=None): +def rest_format_section(section, options, doc=None): """format an options section using as ReST formatted output""" result = "" if section: @@ -388,7 +388,7 @@ def get_full_documentation(self): else: title = "%s options" % section.capitalize() result += get_rest_title(title, "~") - result += "%s\n" % _rest_format_section(None, options) + result += "%s\n" % rest_format_section(None, options) result += """\ Pylint checkers' options and switches ------------------------------------- @@ -436,7 +436,7 @@ def _get_checker_doc(checker, info): result += "%s\n\n" % cleandoc(doc) if options: result += get_rest_title("{} Options".format(checker_title), "^") - result += "%s\n" % _rest_format_section(None, options) + result += "%s\n" % rest_format_section(None, options) if msgs: result += get_rest_title("{} Messages".format(checker_title), "^") for msgid, msg in sorted( From b473ef86bb91d639a20754adc4f7ab83f2241de5 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 10 Jun 2019 08:35:57 +0200 Subject: [PATCH 0159/5147] Refactor - Move utility function to utils We want to use them in Checker too. --- pylint/message/message_handler_mix_in.py | 31 +++--------------------- pylint/utils/__init__.py | 2 ++ pylint/utils/utils.py | 25 +++++++++++++++++++ 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 7676a7f507..c6bdd14acc 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -22,39 +22,14 @@ from pylint.exceptions import InvalidMessageError, UnknownMessageError from pylint.interfaces import UNDEFINED from pylint.message.message import Message -from pylint.utils.utils import ( - _format_option_value, +from pylint.utils import ( category_id, get_module_and_frameid, - normalize_text, + get_rest_title, + rest_format_section, ) -def get_rest_title(title, character): - """Permit to get a rest title underlined with a choosen character.""" - return "%s\n%s\n" % (title, character * len(title)) - - -def rest_format_section(section, options, doc=None): - """format an options section using as ReST formatted output""" - result = "" - if section: - result += get_rest_title(section, "'") - if doc: - formatted_doc = normalize_text(doc, line_len=79, indent="") - result += "%s\n\n" % formatted_doc - for optname, optdict, value in options: - help_opt = optdict.get("help") - result += ":%s:\n" % optname - if help_opt: - formatted_help = normalize_text(help_opt, line_len=79, indent=" ") - result += "%s\n" % formatted_help - if value: - value = str(_format_option_value(optdict, value)) - result += "\n Default: ``%s``\n" % value.replace("`` ", "```` ``") - return result - - class MessagesHandlerMixIn: """a mix-in class containing all the messages related methods for the main lint class diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index 67b56db3a8..8f1a8e4d65 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -56,8 +56,10 @@ format_section, get_global_option, get_module_and_frameid, + get_rest_title, normalize_text, register_plugins, + rest_format_section, safe_decode, tokenize_module, ) diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index ab28a07b5b..c2216575f0 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -51,6 +51,31 @@ def category_id(cid): return MSG_TYPES_LONG.get(cid) +def get_rest_title(title, character): + """Permit to get a rest title underlined with a choosen character.""" + return "%s\n%s\n" % (title, character * len(title)) + + +def rest_format_section(section, options, doc=None): + """format an options section using as ReST formatted output""" + result = "" + if section: + result += get_rest_title(section, "'") + if doc: + formatted_doc = normalize_text(doc, line_len=79, indent="") + result += "%s\n\n" % formatted_doc + for optname, optdict, value in options: + help_opt = optdict.get("help") + result += ":%s:\n" % optname + if help_opt: + formatted_help = normalize_text(help_opt, line_len=79, indent=" ") + result += "%s\n" % formatted_help + if value: + value = str(_format_option_value(optdict, value)) + result += "\n Default: ``%s``\n" % value.replace("`` ", "```` ``") + return result + + def safe_decode(line, encoding, *args, **kwargs): """return decoded line from encoding or decode with default encoding""" try: From 6a1d8db4f27a30c2cc53bd336ed6db957e736c07 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 10 Jun 2019 08:52:48 +0200 Subject: [PATCH 0160/5147] Refactor - Use get_rest_title where it could be --- doc/exts/pylint_extensions.py | 7 ++++--- pylint/message/message_handler_mix_in.py | 9 +++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/doc/exts/pylint_extensions.py b/doc/exts/pylint_extensions.py index 6f2eaa5c47..f740ac5ce8 100755 --- a/doc/exts/pylint_extensions.py +++ b/doc/exts/pylint_extensions.py @@ -8,11 +8,11 @@ import re import sys -import pkg_resources import sphinx from pylint.constants import MAIN_CHECKER_NAME from pylint.lint import PyLinter +from pylint.utils import get_rest_title # Some modules have been renamed and deprecated under their old names. # Skip documenting these modules since: @@ -51,8 +51,9 @@ def builder_inited(app): base_path, "doc", "technical_reference", "extensions.rst" ) with open(extensions_doc, "w") as stream: - stream.write("Optional Pylint checkers in the extensions module\n") - stream.write("=================================================\n\n") + stream.write( + get_rest_title("Optional Pylint checkers in the extensions module", "=") + ) stream.write("Pylint provides the following optional plugins:\n\n") for module in modules: stream.write("- :ref:`{}`\n".format(module)) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index c6bdd14acc..4ef49c551e 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -346,10 +346,8 @@ def _get_checkers_infos(self): return by_checker def get_full_documentation(self): - result = """\ -Pylint global options and switches ----------------------------------- - + result = get_rest_title("Pylint global options and switches", "-") + result += """ Pylint provides global options and switches. """ @@ -364,9 +362,8 @@ def get_full_documentation(self): title = "%s options" % section.capitalize() result += get_rest_title(title, "~") result += "%s\n" % rest_format_section(None, options) + result += get_rest_title("Pylint checkers' options and switches", "-") result += """\ -Pylint checkers' options and switches -------------------------------------- Pylint checkers can provide three set of features: From 19a74597af629dd420e547fe98ee4773757a9afc Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 10 Jun 2019 09:01:15 +0200 Subject: [PATCH 0161/5147] Refactor - Move doc for checkers in BaseChecker At long last. --- pylint/checkers/base_checker.py | 44 +++++++++++++++++++- pylint/message/message_handler_mix_in.py | 52 +++--------------------- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index d4f38886ed..f73f946285 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -11,13 +11,15 @@ # 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 WarningScope +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_rest_title, rest_format_section class BaseChecker(OptionsProviderMixIn): @@ -56,6 +58,46 @@ def __repr__(self): status, self.name, ", ".join(self.msgs.keys()) ) + @staticmethod + def get_full_documentation(info): + result = "" + checker = info.get("checker") + doc = info.get("doc") + module = info.get("module") + msgs = info.get("msgs") + options = info.get("options") + reports = info.get("reports") + checker_title = "%s checker" % (checker.name.replace("_", " ").title()) + if module: + # Provide anchor to link against + result += ".. _%s:\n\n" % module + result += "%s\n" % get_rest_title(checker_title, "~") + if module: + result += "This checker is provided by ``%s``.\n" % module + result += "Verbatim name of the checker is ``%s``.\n\n" % checker.name + if doc: + # Provide anchor to link against + result += get_rest_title("{} Documentation".format(checker_title), "^") + result += "%s\n\n" % cleandoc(doc) + if options: + result += get_rest_title("{} Options".format(checker_title), "^") + result += "%s\n" % rest_format_section(None, options) + if msgs: + result += get_rest_title("{} Messages".format(checker_title), "^") + for msgid, msg in sorted( + msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) + ): + msg = checker.create_message_definition_from_tuple(msgid, msg) + result += "%s\n" % msg.format_help(checkerref=False) + result += "\n" + if reports: + result += get_rest_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 ): diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 4ef49c551e..32b04112cc 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -6,10 +6,8 @@ from __future__ import print_function import sys -from inspect import cleandoc from pylint.constants import ( - _MSG_ORDER, _SCOPE_EXEMPT, MAIN_CHECKER_NAME, MSG_STATE_CONFIDENCE, @@ -375,8 +373,9 @@ def get_full_documentation(self): """ by_checker = self._get_checkers_infos() - for checker, information in sorted(by_checker.items()): - result += self._get_checker_doc(checker, information) + for checker_name, information in sorted(by_checker.items()): + checker = information["checker"] + result += checker.get_full_documentation(information) return result def print_full_documentation(self, stream=None): @@ -386,51 +385,12 @@ def print_full_documentation(self, stream=None): print(self.get_full_documentation()[:-1], file=stream) @staticmethod - def _get_checker_doc(checker, info): - result = "" - checker = info.get("checker") - doc = info.get("doc") - module = info.get("module") - msgs = info.get("msgs") - options = info.get("options") - reports = info.get("reports") - checker_title = "%s checker" % (checker.name.replace("_", " ").title()) - if module: - # Provide anchor to link against - result += ".. _%s:\n\n" % module - result += "%s\n" % get_rest_title(checker_title, "~") - if module: - result += "This checker is provided by ``%s``.\n" % module - result += "Verbatim name of the checker is ``%s``.\n\n" % checker.name - if doc: - # Provide anchor to link against - result += get_rest_title("{} Documentation".format(checker_title), "^") - result += "%s\n\n" % cleandoc(doc) - if options: - result += get_rest_title("{} Options".format(checker_title), "^") - result += "%s\n" % rest_format_section(None, options) - if msgs: - result += get_rest_title("{} Messages".format(checker_title), "^") - for msgid, msg in sorted( - msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) - ): - msg = checker.create_message_definition_from_tuple(msgid, msg) - result += "%s\n" % msg.format_help(checkerref=False) - result += "\n" - if reports: - result += get_rest_title("{} Reports".format(checker_title), "^") - for report in reports: - result += ":%s: %s\n" % report[:2] - result += "\n" - result += "\n" - return result - - @staticmethod - def _print_checker_doc(checker, info, stream=None): + def _print_checker_doc(checker, information, stream=None): """Helper method for print_full_documentation. Also used by doc/exts/pylint_extensions.py. """ if not stream: stream = sys.stdout - print(MessagesHandlerMixIn._get_checker_doc(checker, info)[:-1], file=stream) + checker = information["checker"] + print(checker.get_full_documentation(information)[:-1], file=stream) From ea49905fe6cf0ad87c0d66297be8c8657a0d2886 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 10 Jun 2019 09:22:49 +0200 Subject: [PATCH 0162/5147] Refactor - Remove useless parameter in print_checker_doc --- doc/exts/pylint_extensions.py | 4 ++-- pylint/message/message_handler_mix_in.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/exts/pylint_extensions.py b/doc/exts/pylint_extensions.py index f740ac5ce8..01ba8a2b1c 100755 --- a/doc/exts/pylint_extensions.py +++ b/doc/exts/pylint_extensions.py @@ -68,8 +68,8 @@ def builder_inited(app): "pylint.extensions.docstyle\n\n" ) by_checker = get_plugins_info(linter, doc_files) - for checker, info in sorted(by_checker.items()): - linter._print_checker_doc(checker, info, stream=stream) + for checker, information in sorted(by_checker.items()): + linter._print_checker_doc(information, stream=stream) def get_plugins_info(linter, doc_files): diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 32b04112cc..c34aceee05 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -385,7 +385,7 @@ def print_full_documentation(self, stream=None): print(self.get_full_documentation()[:-1], file=stream) @staticmethod - def _print_checker_doc(checker, information, stream=None): + def _print_checker_doc(information, stream=None): """Helper method for print_full_documentation. Also used by doc/exts/pylint_extensions.py. From 0ee1d5018849e55ee57b04c749992eaa31a29da7 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 10 Jun 2019 09:25:17 +0200 Subject: [PATCH 0163/5147] Fix - W0612: Unused variable 'checker_name' --- pylint/message/message_handler_mix_in.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index c34aceee05..1c8ab400ba 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -373,7 +373,8 @@ def get_full_documentation(self): """ by_checker = self._get_checkers_infos() - for checker_name, information in sorted(by_checker.items()): + for checker in sorted(by_checker): + information = by_checker[checker] checker = information["checker"] result += checker.get_full_documentation(information) return result From 8c30d86abc66f4e5003b01055ec398d6589a6007 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 10 Jun 2019 10:10:44 +0200 Subject: [PATCH 0164/5147] Fix - Remove invalid syntax in python 3.4 Mypy requirements and python 3.4 seem incompatible. --- pylint/checkers/base_checker.py | 2 +- pylint/message/message_handler_mix_in.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index f73f946285..ca5f88082b 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -105,7 +105,7 @@ def add_message( confidence = UNDEFINED self.linter.add_message(msgid, line, node, args, confidence, col_offset) - def check_consistency(self) -> None: + 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 diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 1c8ab400ba..25f862f9e6 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -325,7 +325,7 @@ def add_one_message( ) def _get_checkers_infos(self): - by_checker: dict = {} + by_checker = {} for checker in self.get_checkers(): name = checker.name if name != "master": From 30ba9f4756ed67d6779105b4ee741269501906c2 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 10 Jun 2019 10:37:32 +0200 Subject: [PATCH 0165/5147] Refactor - Clearer function name ('rest' -> 'rst') in utils I used the old name but there was probably a typo in it, as the format is called rst. --- doc/exts/pylint_extensions.py | 4 ++-- pylint/checkers/base_checker.py | 14 +++++++------- pylint/message/message_handler_mix_in.py | 12 ++++++------ pylint/utils/__init__.py | 4 ++-- pylint/utils/utils.py | 10 +++++----- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/doc/exts/pylint_extensions.py b/doc/exts/pylint_extensions.py index 01ba8a2b1c..c12c4fcdd7 100755 --- a/doc/exts/pylint_extensions.py +++ b/doc/exts/pylint_extensions.py @@ -12,7 +12,7 @@ from pylint.constants import MAIN_CHECKER_NAME from pylint.lint import PyLinter -from pylint.utils import get_rest_title +from pylint.utils import get_rst_title # Some modules have been renamed and deprecated under their old names. # Skip documenting these modules since: @@ -52,7 +52,7 @@ def builder_inited(app): ) with open(extensions_doc, "w") as stream: stream.write( - get_rest_title("Optional Pylint checkers in the extensions module", "=") + get_rst_title("Optional Pylint checkers in the extensions module", "=") ) stream.write("Pylint provides the following optional plugins:\n\n") for module in modules: diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index ca5f88082b..fcc5fb7af3 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -19,7 +19,7 @@ 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_rest_title, rest_format_section +from pylint.utils import get_rst_section, get_rst_title class BaseChecker(OptionsProviderMixIn): @@ -71,19 +71,19 @@ def get_full_documentation(info): if module: # Provide anchor to link against result += ".. _%s:\n\n" % module - result += "%s\n" % get_rest_title(checker_title, "~") + 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" % checker.name if doc: # Provide anchor to link against - result += get_rest_title("{} Documentation".format(checker_title), "^") + result += get_rst_title("{} Documentation".format(checker_title), "^") result += "%s\n\n" % cleandoc(doc) if options: - result += get_rest_title("{} Options".format(checker_title), "^") - result += "%s\n" % rest_format_section(None, options) + result += get_rst_title("{} Options".format(checker_title), "^") + result += "%s\n" % get_rst_section(None, options) if msgs: - result += get_rest_title("{} Messages".format(checker_title), "^") + 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]) ): @@ -91,7 +91,7 @@ def get_full_documentation(info): result += "%s\n" % msg.format_help(checkerref=False) result += "\n" if reports: - result += get_rest_title("{} Reports".format(checker_title), "^") + result += get_rst_title("{} Reports".format(checker_title), "^") for report in reports: result += ":%s: %s\n" % report[:2] result += "\n" diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 25f862f9e6..220efb2ad2 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -23,8 +23,8 @@ from pylint.utils import ( category_id, get_module_and_frameid, - get_rest_title, - rest_format_section, + get_rst_section, + get_rst_title, ) @@ -344,7 +344,7 @@ def _get_checkers_infos(self): return by_checker def get_full_documentation(self): - result = get_rest_title("Pylint global options and switches", "-") + result = get_rst_title("Pylint global options and switches", "-") result += """ Pylint provides global options and switches. @@ -358,9 +358,9 @@ def get_full_documentation(self): title = "General options" else: title = "%s options" % section.capitalize() - result += get_rest_title(title, "~") - result += "%s\n" % rest_format_section(None, options) - result += get_rest_title("Pylint checkers' options and switches", "-") + result += get_rst_title(title, "~") + result += "%s\n" % get_rst_section(None, options) + result += get_rst_title("Pylint checkers' options and switches", "-") result += """\ Pylint checkers can provide three set of features: diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py index 8f1a8e4d65..afde963925 100644 --- a/pylint/utils/__init__.py +++ b/pylint/utils/__init__.py @@ -56,10 +56,10 @@ format_section, get_global_option, get_module_and_frameid, - get_rest_title, + get_rst_section, + get_rst_title, normalize_text, register_plugins, - rest_format_section, safe_decode, tokenize_module, ) diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index c2216575f0..81cbc34484 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -51,16 +51,16 @@ def category_id(cid): return MSG_TYPES_LONG.get(cid) -def get_rest_title(title, character): - """Permit to get a rest title underlined with a choosen character.""" +def get_rst_title(title, character): + """Permit to get a title formatted as ReStructuredText test (underlined with a chosen character).""" return "%s\n%s\n" % (title, character * len(title)) -def rest_format_section(section, options, doc=None): - """format an options section using as ReST formatted output""" +def get_rst_section(section, options, doc=None): + """format an options section using as a ReStructuredText formatted output""" result = "" if section: - result += get_rest_title(section, "'") + result += get_rst_title(section, "'") if doc: formatted_doc = normalize_text(doc, line_len=79, indent="") result += "%s\n\n" % formatted_doc From 801ed4f4117e056d1a6ebc95b07127a4b1d8750f Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Mon, 10 Jun 2019 21:49:16 +0200 Subject: [PATCH 0166/5147] Refactor - Use self directly in checker get_full_documentation --- pylint/checkers/base_checker.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index fcc5fb7af3..2e9c027347 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -58,23 +58,21 @@ def __repr__(self): status, self.name, ", ".join(self.msgs.keys()) ) - @staticmethod - def get_full_documentation(info): + def get_full_documentation(self, info): result = "" - checker = info.get("checker") doc = info.get("doc") module = info.get("module") msgs = info.get("msgs") options = info.get("options") reports = info.get("reports") - checker_title = "%s checker" % (checker.name.replace("_", " ").title()) + 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" % checker.name + 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), "^") @@ -87,7 +85,7 @@ def get_full_documentation(info): for msgid, msg in sorted( msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1]) ): - msg = checker.create_message_definition_from_tuple(msgid, msg) + msg = self.create_message_definition_from_tuple(msgid, msg) result += "%s\n" % msg.format_help(checkerref=False) result += "\n" if reports: From bed2484b2ea7341661e4cecc622b1d67e58d1d2c Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Wed, 12 Jun 2019 20:28:23 +0200 Subject: [PATCH 0167/5147] Refactor - Giving multiple parameters instead of a dict This make the understanding of the function easier. --- pylint/checkers/base_checker.py | 7 +------ pylint/message/message_handler_mix_in.py | 6 ++++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index 2e9c027347..f308f15574 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -58,13 +58,8 @@ def __repr__(self): status, self.name, ", ".join(self.msgs.keys()) ) - def get_full_documentation(self, info): + def get_full_documentation(self, msgs, options, reports, doc=None, module=None): result = "" - doc = info.get("doc") - module = info.get("module") - msgs = info.get("msgs") - options = info.get("options") - reports = info.get("reports") checker_title = "%s checker" % (self.name.replace("_", " ").title()) if module: # Provide anchor to link against diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 220efb2ad2..8f1375b1e0 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -376,7 +376,8 @@ def get_full_documentation(self): for checker in sorted(by_checker): information = by_checker[checker] checker = information["checker"] - result += checker.get_full_documentation(information) + del information["checker"] + result += checker.get_full_documentation(**information) return result def print_full_documentation(self, stream=None): @@ -394,4 +395,5 @@ def _print_checker_doc(information, stream=None): if not stream: stream = sys.stdout checker = information["checker"] - print(checker.get_full_documentation(information)[:-1], file=stream) + del information["checker"] + print(checker.get_full_documentation(**information)[:-1], file=stream) From c4144c8548f873434a288b305e7ba405098d9706 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Wed, 12 Jun 2019 21:20:59 +0200 Subject: [PATCH 0168/5147] Feat - Add a __str__ function to BaseChecker --- pylint/checkers/base_checker.py | 9 +++++ tests/unittest_checker_base.py | 62 ++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index f308f15574..8229aab056 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -58,6 +58,13 @@ def __repr__(self): 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()) @@ -72,6 +79,8 @@ def get_full_documentation(self, msgs, options, reports, doc=None, module=None): # 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) diff --git a/tests/unittest_checker_base.py b/tests/unittest_checker_base.py index 1feae5b7ac..b50fbf1a65 100644 --- a/tests/unittest_checker_base.py +++ b/tests/unittest_checker_base.py @@ -23,7 +23,7 @@ import astroid -from pylint.checkers import base +from pylint.checkers import BaseChecker, base from pylint.testutils import CheckerTestCase, Message, set_config @@ -526,3 +526,63 @@ def test_pascal_case(self): self._test_name_is_incorrect_for_all_name_types(naming_style, name) self._test_should_always_pass(naming_style) + + +class TestBaseChecker(unittest.TestCase): + def test_doc(self): + class OtherBasicChecker(BaseChecker): + name = "basic" + msgs = { + "W0001": ( + "Basic checker has an example.", + "basic-checker-example", + "Used nowhere and serves no purpose.", + ) + } + + class LessBasicChecker(OtherBasicChecker): + options = ( + ( + "example-args", + { + "default": 42, + "type": "int", + "metavar": "", + "help": "Example of integer argument for the checker.", + }, + ), + ) + + basic = OtherBasicChecker() + expected_beginning = """\ +Basic checker +~~~~~~~~~~~~~ + +Verbatim name of the checker is ``basic``. + +""" + expected_middle = """\ +Basic checker Options +^^^^^^^^^^^^^^^^^^^^^ +:example-args: + Example of integer argument for the checker. + + Default: ``42`` + +""" + expected_end = """\ +Basic checker Messages +^^^^^^^^^^^^^^^^^^^^^^ +:basic-checker-example (W0001): *Basic checker has an example.* + Used nowhere and serves no purpose. + + +""" + self.assertEqual(str(basic), expected_beginning + expected_end) + self.assertEqual(repr(basic), "Checker 'basic' responsible for W0001") + less_basic = LessBasicChecker() + + self.assertEqual( + str(less_basic), expected_beginning + expected_middle + expected_end + ) + self.assertEqual(repr(less_basic), repr(basic)) From 569421ca60d82ae984a6f52d76ec8d7397aac437 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Thu, 20 Jun 2019 13:00:51 +0200 Subject: [PATCH 0169/5147] Refactor - Remove unused argument 'name' --- doc/exts/pylint_extensions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/exts/pylint_extensions.py b/doc/exts/pylint_extensions.py index c12c4fcdd7..157850837b 100755 --- a/doc/exts/pylint_extensions.py +++ b/doc/exts/pylint_extensions.py @@ -93,7 +93,6 @@ def get_plugins_info(linter, doc_files): by_checker[checker]["msgs"].update(checker.msgs) by_checker[checker]["reports"] += checker.reports by_checker[checker]["doc"] += doc - by_checker[checker]["name"] += checker.name by_checker[checker]["module"] += module except KeyError: by_checker[checker] = { @@ -102,7 +101,6 @@ def get_plugins_info(linter, doc_files): "msgs": dict(checker.msgs), "reports": list(checker.reports), "doc": doc, - "name": checker.name, "module": module, } return by_checker From 298a22d96ca512ab3910303ae8f2913e608d0a64 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Thu, 20 Jun 2019 13:04:46 +0200 Subject: [PATCH 0170/5147] Refactor - Rename an overlapsing function in MessageHandlerMixIn BaseChecker and MessageHandlerMixIn can be the same instance. --- pylint/message/message_handler_mix_in.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 8f1375b1e0..06e1368bac 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -343,7 +343,7 @@ def _get_checkers_infos(self): } return by_checker - def get_full_documentation(self): + def get_checkers_documentation(self): result = get_rst_title("Pylint global options and switches", "-") result += """ Pylint provides global options and switches. @@ -384,7 +384,7 @@ def print_full_documentation(self, stream=None): """output a full documentation in ReST format""" if not stream: stream = sys.stdout - print(self.get_full_documentation()[:-1], file=stream) + print(self.get_checkers_documentation()[:-1], file=stream) @staticmethod def _print_checker_doc(information, stream=None): From 0eeb67749b6aa016db61d87e55a1f7a7e459b352 Mon Sep 17 00:00:00 2001 From: Andrzej Klajnert Date: Wed, 26 Jun 2019 17:36:28 +0200 Subject: [PATCH 0171/5147] Fix - add `__post_init__` into `defining-attr-methods` to avoid `attribute-defined-outside-init` in dataclasses. --- CONTRIBUTORS.txt | 2 ++ ChangeLog | 4 ++++ examples/pylintrc | 3 ++- pylint/checkers/classes.py | 2 +- pylintrc | 2 +- tests/functional/attribute_defined_outside_init.py | 4 ++++ 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 8d885176b8..4a6ff2f9e0 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -305,3 +305,5 @@ contributors: * Agustin Toledo: contributor * Nicholas Smith: contributor + +* Andrzej Klajnert: contributor diff --git a/ChangeLog b/ChangeLog index 69d517ea26..2076131af3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -158,6 +158,10 @@ Release date: TBA Close #2963 +* Added ``__post_init__`` to ``defining-attr-methods`` in order to avoid ``attribute-defined-outside-init`` in dataclasses. + + Close #2581 + What's New in Pylint 2.3.0? =========================== diff --git a/examples/pylintrc b/examples/pylintrc index c2a1558ce7..2e35e3b89d 100644 --- a/examples/pylintrc +++ b/examples/pylintrc @@ -460,7 +460,8 @@ redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__, __new__, - setUp + setUp, + __post_init__ # List of member names, which should be excluded from the protected access # warning. diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index a62521aba1..8b1da11d6e 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -666,7 +666,7 @@ class ClassChecker(BaseChecker): ( "defining-attr-methods", { - "default": ("__init__", "__new__", "setUp"), + "default": ("__init__", "__new__", "setUp", "__post_init__"), "type": "csv", "metavar": "", "help": "List of method names used to declare (i.e. assign) \ diff --git a/pylintrc b/pylintrc index 3475fb7673..dbd5265526 100644 --- a/pylintrc +++ b/pylintrc @@ -334,7 +334,7 @@ max-public-methods=25 [CLASSES] # List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp +defining-attr-methods=__init__,__new__,setUp,__post_init__ # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls diff --git a/tests/functional/attribute_defined_outside_init.py b/tests/functional/attribute_defined_outside_init.py index ff31e955db..1cb319c617 100644 --- a/tests/functional/attribute_defined_outside_init.py +++ b/tests/functional/attribute_defined_outside_init.py @@ -78,3 +78,7 @@ def prop(self): @prop.setter def prop(self, value): self.__prop = value + +class DataClass: + def __post_init__(self): + self.a = 42 From 02297960ac482c66c89b208b331f863ae8a2d09e Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sat, 6 Jul 2019 18:38:19 +0300 Subject: [PATCH 0172/5147] Excluded `attrs` from `too-few-public-methods` check. Close #2988. --- ChangeLog | 2 ++ pylint/checkers/design_analysis.py | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2076131af3..5bd3458c95 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,8 @@ What's New in Pylint 2.4.0? Release date: TBA +* Excluded `attrs` from `too-few-public-methods` check. Close #2988. + * ``unused-import`` emitted for the right import names in function scopes. Close #2928 diff --git a/pylint/checkers/design_analysis.py b/pylint/checkers/design_analysis.py index b6fd9913ce..62a718098e 100644 --- a/pylint/checkers/design_analysis.py +++ b/pylint/checkers/design_analysis.py @@ -88,7 +88,7 @@ ), } SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$") -DATACLASS_DECORATOR = "dataclass" +DATACLASSES_DECORATORS = frozenset({"dataclass", "attrs"}) DATACLASS_IMPORT = "dataclasses" TYPING_NAMEDTUPLE = "typing.NamedTuple" @@ -126,19 +126,24 @@ def _is_enum_class(node: astroid.ClassDef) -> bool: return False -def _is_dataclass(node: astroid.ClassDef) -> bool: - """Check if a class definition defines a Python 3.7+ dataclass +def _is_dataclass_like(node: astroid.ClassDef) -> bool: + """Check if a class definition defines a Python data class + + A list of decorator names are introspected, such as the builtin + `dataclass` decorator, as well as the popular `attrs` one from + the `attrs` library. :param node: The class node to check. :type node: astroid.ClassDef - :returns: True if the given node represents a dataclass class. False otherwise. + :returns: + `True` if the given node represents a dataclass class, `False` otherwise. :rtype: bool """ if not node.decorators: return False - root_locals = node.root().locals + root_locals = set(node.root().locals) for decorator in node.decorators.nodes: if isinstance(decorator, astroid.Call): decorator = decorator.func @@ -148,7 +153,9 @@ def _is_dataclass(node: astroid.ClassDef) -> bool: name = decorator.name else: name = decorator.attrname - if name == DATACLASS_DECORATOR and DATACLASS_DECORATOR in root_locals: + if name in DATACLASSES_DECORATORS and root_locals.intersection( + DATACLASSES_DECORATORS + ): return True return False @@ -361,7 +368,7 @@ def leave_classdef(self, node): if ( node.type != "class" or _is_enum_class(node) - or _is_dataclass(node) + or _is_dataclass_like(node) or _is_typing_namedtuple(node) ): return From 68b1d720dacca07aa5282b1d118d5ab873c5ed15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ois=C3=ADn=20Moran?= Date: Sun, 7 Jul 2019 08:27:20 +0100 Subject: [PATCH 0173/5147] Grammar Fixes (#2974) --- CONTRIBUTORS.txt | 2 ++ doc/tutorial.rst | 4 ++-- pylint/checkers/base.py | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 4a6ff2f9e0..383c958d43 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -306,4 +306,6 @@ contributors: * Nicholas Smith: contributor +* Oisin Moran: contributor + * Andrzej Klajnert: contributor diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 92f48092cc..02e7444bb4 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -168,8 +168,8 @@ command line and try this: robertk01 Desktop$ pylint --help-msg=missing-docstring :missing-docstring (C0111): *Missing %s docstring* - Used when a module, function, class or method has no docstring.Some special - methods like __init__ doesn't necessary require a docstring. This message + Used when a module, function, class or method has no docstring. Some special + methods like __init__ don't necessarily require a docstring. This message belongs to the basic checker. diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 76ef096085..ff77d3d596 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -1872,8 +1872,8 @@ class DocStringChecker(_BasicChecker): "C0111": ( "Missing %s docstring", # W0131 "missing-docstring", - "Used when a module, function, class or method has no docstring." - "Some special methods like __init__ doesn't necessary require a " + "Used when a module, function, class or method has no docstring. " + "Some special methods like __init__ don't necessarily require a " "docstring.", ), "C0112": ( From f7e218bd2ffdcd32ee80cafc2169ec1f2f037364 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 7 Jul 2019 10:57:38 +0300 Subject: [PATCH 0174/5147] Escape the clean path before normalizing to slashes (#2993) --- tests/test_self.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_self.py b/tests/test_self.py index a1e3bb4465..456c4172f0 100644 --- a/tests/test_self.py +++ b/tests/test_self.py @@ -40,7 +40,7 @@ from pylint.reporters.text import * HERE = abspath(dirname(__file__)) -CLEAN_PATH = dirname(dirname(__file__)) + "/" +CLEAN_PATH = re.escape(dirname(dirname(__file__)) + "/") @contextlib.contextmanager From e6cd6e35bae7d19d1264c398dafe21c758fe4747 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 7 Jul 2019 17:13:05 +0300 Subject: [PATCH 0175/5147] Exclude Ellipsis from pointless-statement for Python 3.8 --- pylint/checkers/base.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index ff77d3d596..e28de3bcad 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -1090,7 +1090,7 @@ def visit_classdef(self, node): # pylint: disable=unused-argument "pointless-statement", "pointless-string-statement", "expression-not-assigned" ) def visit_expr(self, node): - """check for various kind of statements without effect""" + """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 @@ -1117,14 +1117,19 @@ def visit_expr(self, node): # Ignore if this is : # * a direct function call # * the unique child of a try/except body - # * a yieldd statement + # * 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] + 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)): From af45f55d862d530e947e685a3812812fd4c61435 Mon Sep 17 00:00:00 2001 From: Peter Kolbus Date: Tue, 9 Jul 2019 01:48:06 -0500 Subject: [PATCH 0176/5147] Improve help for options (#2986) --- CONTRIBUTORS.txt | 2 + ChangeLog | 2 + examples/pylintrc | 474 +++++++++++++++++++----------------- man/pylint.1 | 336 +++++++++++++------------ pylint/checkers/spelling.py | 12 +- pylint/lint.py | 14 +- 6 files changed, 443 insertions(+), 397 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 383c958d43..715807932d 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -306,6 +306,8 @@ contributors: * Nicholas Smith: contributor +* Peter Kolbus (Garmin): contributor + * Oisin Moran: contributor * Andrzej Klajnert: contributor diff --git a/ChangeLog b/ChangeLog index 5bd3458c95..3fbf12fff3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -301,6 +301,8 @@ Release date: 2019-02-27 * Fix false positive ``useless-else-on-loop`` if the break is deep in the else of an inner loop. +* Minor improvements to the help text for a few options. + What's New in Pylint 2.2.2? =========================== diff --git a/examples/pylintrc b/examples/pylintrc index 2e35e3b89d..1277bd21b2 100644 --- a/examples/pylintrc +++ b/examples/pylintrc @@ -2,7 +2,7 @@ # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code +# run arbitrary code. extension-pkg-whitelist= # Add files or directories to the blacklist. They should be base names, not @@ -21,7 +21,12 @@ ignore-patterns= # number of processors available to use. jobs=1 -# List of plugins (as comma separated values of python modules names) to load, +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. load-plugins= @@ -32,7 +37,7 @@ persistent=yes #rcfile= # When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages +# user-friendly hints instead of false-positive error messages. suggestion-mode=yes # Allow loading of arbitrary C extensions. Extensions are imported into the @@ -43,18 +48,18 @@ unsafe-load-any-extension=no [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. confidence= # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to +# file where it should appear only once). You can also use "--disable=all" to # disable everything first and then reenable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". disable=print-statement, parameter-unpacking, unpacking-in-except, @@ -144,15 +149,15 @@ enable=c-extension-no-member [REPORTS] -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'error', 'warning', 'refactor', and 'convention' +# which contain the number of messages in each category, as well as 'statement' +# which is the total number of statements analyzed. This score is used by the +# global evaluation report (RP0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details +# used to format the message information. See doc for all details. #msg-template= # Set the output format. Available formats are text, parseable, colorized, json @@ -160,7 +165,7 @@ evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / stateme # mypackage.mymodule.MyReporterClass. output-format=text -# Tells whether to display a full report or only the messages +# Tells whether to display a full report or only the messages. reports=no # Activate the evaluation score. @@ -179,23 +184,191 @@ max-nested-blocks=5 never-returning-functions=sys.exit +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# 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. +generated-members= + +# 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-mixin-members=yes + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# 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-on-opaque-inference=yes + +# 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. +ignored-classes=optparse.Values,thread._local,_thread._local + +# 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. +ignored-modules= + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[LOGGING] + +# Format style used to check logging format string. `old` means using % +# formatting, while `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# 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. +no-space-check=trailing-comma, + dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + [BASIC] -# Naming style matching correct argument names +# Naming style matching correct argument names. argument-naming-style=snake_case # Regular expression matching correct argument names. Overrides argument- -# naming-style +# naming-style. #argument-rgx= -# Naming style matching correct attribute names +# Naming style matching correct attribute names. attr-naming-style=snake_case # Regular expression matching correct attribute names. Overrides attr-naming- -# style +# style. #attr-rgx= -# Bad variable names which should always be refused, separated by a comma +# Bad variable names which should always be refused, separated by a comma. bad-names=foo, bar, baz, @@ -203,38 +376,39 @@ bad-names=foo, tutu, tata -# Naming style matching correct class attribute names +# Naming style matching correct class attribute names. class-attribute-naming-style=any # Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style +# attribute-naming-style. #class-attribute-rgx= -# Naming style matching correct class names +# Naming style matching correct class names. class-naming-style=PascalCase -# Regular expression matching correct class names. Overrides class-naming-style +# Regular expression matching correct class names. Overrides class-naming- +# style. #class-rgx= -# Naming style matching correct constant names +# Naming style matching correct constant names. const-naming-style=UPPER_CASE # Regular expression matching correct constant names. Overrides const-naming- -# style +# style. #const-rgx= # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 -# Naming style matching correct function names +# Naming style matching correct function names. function-naming-style=snake_case # Regular expression matching correct function names. Overrides function- -# naming-style +# naming-style. #function-rgx= -# Good variable names which should always be accepted, separated by a comma +# Good variable names which should always be accepted, separated by a comma. good-names=i, j, k, @@ -242,28 +416,28 @@ good-names=i, Run, _ -# Include a hint for the correct naming format with invalid-name +# Include a hint for the correct naming format with invalid-name. include-naming-hint=no -# Naming style matching correct inline iteration names +# Naming style matching correct inline iteration names. inlinevar-naming-style=any # Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style +# inlinevar-naming-style. #inlinevar-rgx= -# Naming style matching correct method names +# Naming style matching correct method names. method-naming-style=snake_case # Regular expression matching correct method names. Overrides method-naming- -# style +# style. #method-rgx= -# Naming style matching correct module names +# Naming style matching correct module names. module-naming-style=snake_case # Regular expression matching correct module names. Overrides module-naming- -# style +# style. #module-rgx= # Colon-delimited sets of names that determine each other's naming style when @@ -276,60 +450,17 @@ no-docstring-rgx=^_ # 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. property-classes=abc.abstractproperty -# Naming style matching correct variable names +# Naming style matching correct variable names. variable-naming-style=snake_case # Regular expression matching correct variable names. Overrides variable- -# naming-style +# naming-style. #variable-rgx= -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module -max-module-lines=1000 - -# 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. -no-space-check=trailing-comma, - dict-separator - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. @@ -338,121 +469,48 @@ notes=FIXME, TODO -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# 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. -generated-members= - -# 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-mixin-members=yes - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None -ignore-none=yes - -# 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-on-opaque-inference=yes - -# 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. -ignored-classes=optparse.Values,thread._local,_thread._local - -# 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. -ignored-modules= +[STRING] -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes +# This flag controls whether the implicit-str-concat-in-sequence should +# generate a warning on implicit string concatenation in sequences defined over +# several lines. +check-str-concat-over-line-jumps=no -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 +[IMPORTS] +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no -[VARIABLES] +# 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. +analyse-fallback-blocks=no -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -additional-builtins= +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=optparse,tkinter.tix -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled). +ext-import-graph= -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled). +import-graph= -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled). +int-import-graph= -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.*|^ignored_|^unused_ +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= -# Tells whether we should check for unused import in __init__ files. -init-import=no +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= [CLASSES] @@ -480,7 +538,7 @@ valid-metaclass-classmethod-first-arg=cls [DESIGN] -# Maximum number of arguments for function / method +# Maximum number of arguments for function / method. max-args=5 # Maximum number of attributes for a class (see R0902). @@ -489,10 +547,10 @@ max-attributes=7 # Maximum number of boolean expressions in an if statement (see R0916). max-bool-expr=5 -# Maximum number of branch for function / method body +# Maximum number of branch for function / method body. max-branches=12 -# Maximum number of locals for function / method body +# Maximum number of locals for function / method body. max-locals=15 # Maximum number of parents for a class (see R0901). @@ -501,51 +559,19 @@ max-parents=7 # Maximum number of public methods for a class (see R0904). max-public-methods=20 -# Maximum number of return / yield for function / method body +# Maximum number of return / yield for function / method body. max-returns=6 -# Maximum number of statements in function / method body +# Maximum number of statements in function / method body. max-statements=50 # Minimum number of public methods for a class (see R0903). min-public-methods=2 -[IMPORTS] - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# 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. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - - [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception +# "BaseException, Exception". +overgeneral-exceptions=BaseException, + Exception diff --git a/man/pylint.1 b/man/pylint.1 index 2514171a11..40b7c1cbc9 100644 --- a/man/pylint.1 +++ b/man/pylint.1 @@ -1,4 +1,4 @@ -.TH pylint 1 "2018-06-13" pylint +.TH pylint 1 "2019-06-29" pylint .SH NAME .B pylint \- python code static checker @@ -43,11 +43,11 @@ Specify a configuration file. .IP "--init-hook=" Python code to execute, usually for sys.path manipulation such as pygtk.require(). .IP "--errors-only, -E" -In error mode, checkers without error messages are disabled and for others, only the ERROR messages are displayed, and no reports are done by default +In error mode, checkers without error messages are disabled and for others, only the ERROR messages are displayed, and no reports are done by default. .IP "--py3k" -In Python 3 porting mode, all checkers will be disabled and only messages emitted by the porting checker will be displayed +In Python 3 porting mode, all checkers will be disabled and only messages emitted by the porting checker will be displayed. .IP "--verbose, -v" -In verbose mode, extra non-checker-related info will be displayed +In verbose mode, extra non-checker-related info will be displayed. .IP "--ignore=[,...]" Add files or directories to the blacklist. They should be base names, not paths. [default: CVS] .IP "--ignore-patterns=[,...]" @@ -55,21 +55,27 @@ Add files or directories matching the regex patterns to the blacklist. The regex .IP "--persistent=" Pickle collected data for later comparisons. [default: yes] .IP "--load-plugins=" -List of plugins (as comma separated values of python modules names) to load, usually to register additional checkers. [default: none] +List of plugins (as comma separated values of python module names) to load, usually to register additional checkers. [default: none] .IP "--jobs=, -j " Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the number of processors available to use. [default: 1] +.IP "--limit-inference-results=" +Control the amount of potential inferred values when inferring a single object. This can help the performance when dealing with large functions or complex, nested conditions. [default: 100] .IP "--extension-pkg-whitelist=" -A comma-separated list of package or module names from where C extensions may be loaded. Extensions are loading into the active Python interpreter and may run arbitrary code [default: none] +A comma-separated list of package or module names from where C extensions may be loaded. Extensions are loading into the active Python interpreter and may run arbitrary code. [default: none] .IP "--suggestion-mode=" -When enabled, pylint would attempt to guess common misconfiguration and emit user-friendly hints instead of false-positive error messages [default: yes] +When enabled, pylint would attempt to guess common misconfiguration and emit user-friendly hints instead of false-positive error messages. [default: yes] .IP "--exit-zero" Always return a 0 (non-error) status code, even if lint errors are found. This is primarily useful in continuous integration scripts. +.IP "--from-stdin" +Interpret the stdin as a python script, whose filename needs to be passed as the module_or_package argument. .SH COMMANDS .IP "--help-msg=" Display a help message for the given message id and exit. The value may be a comma separated list of message ids. .IP "--list-msgs" Generate pylint's messages. +.IP "--list-groups" +List pylint's message groups. .IP "--list-conf-levels" Generate pylint's confidence levels. .IP "--full-documentation" @@ -79,101 +85,217 @@ Generate a sample configuration file according to the current configuration. You .SH MESSAGES CONTROL .IP "--confidence=" -Only show warnings with the listed confidence levels. Leave empty to show all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED [default: none] +Only show warnings with the listed confidence levels. Leave empty to show all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. [default: none] .IP "--enable=, -e " Enable the message, report, category or checker with the given id(s). You can either give multiple identifier separated by comma (,) or put this option multiple time (only on the command line, not in the configuration file where it should appear only once). See also the "--disable" option for examples. .IP "--disable=, -d " -Disable the message, report, category or checker with the given id(s). You can either give multiple identifiers separated by comma (,) or put this option multiple times (only on the command line, not in the configuration file where it should appear only once).You can also use "--disable=all" to disable everything first and then reenable specific checks. For example, if you want to run only the similarities checker, you can use "--disable=all --enable=similarities". If you want to run only the classes checker, but have no Warning level messages displayed, use"--disable=all --enable=classes --disable=W" +Disable the message, report, category or checker with the given id(s). You can either give multiple identifiers separated by comma (,) or put this option multiple times (only on the command line, not in the configuration file where it should appear only once). You can also use "--disable=all" to disable everything first and then reenable specific checks. For example, if you want to run only the similarities checker, you can use "--disable=all --enable=similarities". If you want to run only the classes checker, but have no Warning level messages displayed, use "--disable=all --enable=classes --disable=W". .SH REPORTS .IP "--output-format=, -f " Set the output format. Available formats are text, parseable, colorized, json and msvs (visual studio). You can also give a reporter class, e.g. mypackage.mymodule.MyReporterClass. [default: text] .IP "--reports=, -r " -Tells whether to display a full report or only the messages [default: no] +Tells whether to display a full report or only the messages. [default: no] .IP "--evaluation=" -Python expression which should return a note less than 10 (10 is the highest note). You have access to the variables errors warning, statement which respectively contain the number of errors / warnings messages and the total number of statements analyzed. This is used by the global evaluation report (RP0004). [default: 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)] +Python expression which should return a score less than or equal to 10. You have access to the variables 'error', 'warning', 'refactor', and 'convention' which contain the number of messages in each category, as well as 'statement' which is the total number of statements analyzed. This score is used by the global evaluation report (RP0004). [default: 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)] .IP "--score=, -s " Activate the evaluation score. [default: yes] .IP "--msg-template=