From 5ebbc30afbaa1a3c1e04fc86c3525edf9f579640 Mon Sep 17 00:00:00 2001 From: yushao2 <36848472+yushao2@users.noreply.github.com> Date: Mon, 24 May 2021 04:48:39 +0800 Subject: [PATCH] Implemented new check consider-using-from-import (#4491) --- ChangeLog | 5 +++ doc/whatsnew/2.9.rst | 2 ++ pylint/checkers/imports.py | 36 +++++++++++++------ tests/extensions/test_empty_comment.py | 2 +- .../import_aliasing.py} | 8 +++-- tests/functional/i/import_aliasing.txt | 9 +++++ .../u/useless/useless-import-alias.txt | 7 ---- 7 files changed, 48 insertions(+), 21 deletions(-) rename tests/functional/{u/useless/useless-import-alias.py => i/import_aliasing.py} (80%) create mode 100644 tests/functional/i/import_aliasing.txt delete mode 100644 tests/functional/u/useless/useless-import-alias.txt diff --git a/ChangeLog b/ChangeLog index d57843e2f4..905873ebfd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -67,6 +67,11 @@ modules are added. Closes #4470 +* New checker``consider-using-from-import``. Emitted when a submodule/member of a package is imported and aliased + with the same name. + + Closes #2309 + What's New in Pylint 2.8.2? =========================== diff --git a/doc/whatsnew/2.9.rst b/doc/whatsnew/2.9.rst index 3ea0e19407..644e9a648d 100644 --- a/doc/whatsnew/2.9.rst +++ b/doc/whatsnew/2.9.rst @@ -23,6 +23,8 @@ New checkers * ``unnecessary-dict-index-lookup``: Emitted when iterating over dictionary items (key-value pairs) and accessing the value by index lookup. +* ``consider-using-from-import``: Emitted when a submodule/member of a package is imported and aliased with the same name. + Other Changes ============= diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index 73fc4cbbdf..7f5b48d9aa 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -48,7 +48,7 @@ import os import sys from distutils import sysconfig -from typing import Dict, List +from typing import Dict, List, Union import astroid @@ -236,6 +236,14 @@ def _make_graph(filename: str, dep_info: Dict[str, List[str]], sect: VNode, gtyp "cyclic-import", "Used when a cyclic import between two or more modules is detected.", ), + "R0402": ( + "Use 'from %s import %s' instead", + "consider-using-from-import", + "Emitted when a submodule/member of a package is imported and " + "aliased with the same name. " + "E.g., instead of ``import pandas.DataFrame as DataFrame`` use " + "``from pandas import DataFrame``", + ), "W0401": ( "Wildcard import %s", "wildcard-import", @@ -865,22 +873,28 @@ def _check_preferred_module(self, node, mod_path): args=(self.preferred_modules[mod_path], mod_path), ) - def _check_import_as_rename(self, node): + def _check_import_as_rename( + self, node: Union[astroid.Import, astroid.ImportFrom] + ) -> None: names = node.names for name in names: if not all(name): return - real_name = name[0] - splitted_packages = real_name.rsplit(".") - real_name = splitted_packages[-1] - imported_name = name[1] - # consider only following cases - # import x as x - # and ignore following - # import x.y.z as z - if real_name == imported_name and len(splitted_packages) == 1: + splitted_packages = name[0].rsplit(".", maxsplit=1) + import_name = splitted_packages[-1] + aliased_name = name[1] + if import_name != aliased_name: + continue + + if len(splitted_packages) == 1: self.add_message("useless-import-alias", node=node) + elif len(splitted_packages) == 2: + self.add_message( + "consider-using-from-import", + node=node, + args=(splitted_packages[0], import_name), + ) def _check_reimport(self, node, basename=None, level=None): """check if the import is necessary (i.e. not already done)""" diff --git a/tests/extensions/test_empty_comment.py b/tests/extensions/test_empty_comment.py index f4e2ed9477..b53f31d2f4 100644 --- a/tests/extensions/test_empty_comment.py +++ b/tests/extensions/test_empty_comment.py @@ -2,7 +2,7 @@ import pytest -import pylint.extensions.empty_comment as empty_comment +from pylint.extensions import empty_comment @pytest.fixture(scope="module") diff --git a/tests/functional/u/useless/useless-import-alias.py b/tests/functional/i/import_aliasing.py similarity index 80% rename from tests/functional/u/useless/useless-import-alias.py rename to tests/functional/i/import_aliasing.py index c8ef8374de..abe5087e15 100644 --- a/tests/functional/u/useless/useless-import-alias.py +++ b/tests/functional/i/import_aliasing.py @@ -1,9 +1,13 @@ # pylint: disable=unused-import, missing-docstring, invalid-name, reimported, import-error, wrong-import-order, no-name-in-module, relative-beyond-top-level +# Functional tests for import aliasing +# 1. useless-import-alias +# 2. consider-using-from-import + from collections import OrderedDict as OrderedDict # [useless-import-alias] from collections import OrderedDict as o_dict -import os.path as path +import os.path as path # [consider-using-from-import] import os.path as p -import foo.bar.foobar as foobar +import foo.bar.foobar as foobar # [consider-using-from-import] import os import os as OS from sys import version diff --git a/tests/functional/i/import_aliasing.txt b/tests/functional/i/import_aliasing.txt new file mode 100644 index 0000000000..e95cb3365e --- /dev/null +++ b/tests/functional/i/import_aliasing.txt @@ -0,0 +1,9 @@ +useless-import-alias:6:0::Import alias does not rename original package +consider-using-from-import:8:0::Use 'from os import path' instead +consider-using-from-import:10:0::Use 'from foo.bar import foobar' instead +useless-import-alias:14:0::Import alias does not rename original package +useless-import-alias:17:0::Import alias does not rename original package +useless-import-alias:18:0::Import alias does not rename original package +useless-import-alias:20:0::Import alias does not rename original package +useless-import-alias:21:0::Import alias does not rename original package +useless-import-alias:23:0::Import alias does not rename original package diff --git a/tests/functional/u/useless/useless-import-alias.txt b/tests/functional/u/useless/useless-import-alias.txt deleted file mode 100644 index bc99952ace..0000000000 --- a/tests/functional/u/useless/useless-import-alias.txt +++ /dev/null @@ -1,7 +0,0 @@ -useless-import-alias:2:0::Import alias does not rename original package -useless-import-alias:10:0::Import alias does not rename original package -useless-import-alias:13:0::Import alias does not rename original package -useless-import-alias:14:0::Import alias does not rename original package -useless-import-alias:16:0::Import alias does not rename original package -useless-import-alias:17:0::Import alias does not rename original package -useless-import-alias:19:0::Import alias does not rename original package