From f5a6d82b7365b90f5338995a50dc128535c97c3f Mon Sep 17 00:00:00 2001 From: Eric Nielsen Date: Sat, 26 Mar 2022 22:14:56 -0500 Subject: [PATCH 1/2] Show hopefully friendlier messages Since relative imports don't work with the `import <>` syntax, suggest importing the names directly. In these cases, the `i.module` is `None` and the `i.level` has the number of levels down in the relative import, e.g. `1` means the current directory. If there's only one name, suggest: use 'import module.name' instead of 'from module import name' This implements the enhancement proposed in #1. --- flake8_import_style/__init__.py | 22 ++++++++++++++++++---- flake8_import_style/tests/__init__.py | 15 ++++++++++++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/flake8_import_style/__init__.py b/flake8_import_style/__init__.py index 9782176..b9ed778 100644 --- a/flake8_import_style/__init__.py +++ b/flake8_import_style/__init__.py @@ -5,7 +5,7 @@ __version__ = pkg_resources.get_distribution(__package__).version -I801 = "I801 use 'import {module}' instead of 'from {module} import {names}'" +I801 = "I801 use 'import {stmt}' instead of 'from {module} import {names}'" class I8(object): @@ -22,7 +22,21 @@ def run(self): if isinstance(i, ast.ImportFrom): if i.module == "__future__": continue - message = I801.format( - module=(i.module or "..."), - names=", ".join(i.name for i in i.names)) + names = ", ".join(i.name for i in i.names) + if i.module is None: + # Relative imports don't work with the `import <>` syntax. + message = I801.format( + stmt=names, + module="." * i.level, + names=names) + elif len(i.names) == 1: + message = I801.format( + stmt="{0}.{1}".format(i.module, names), + module=i.module, + names=names) + else: + message = I801.format( + stmt=i.module, + module=i.module, + names=names) yield (i.lineno, i.col_offset, message, "I801") diff --git a/flake8_import_style/tests/__init__.py b/flake8_import_style/tests/__init__.py index dcd7c2f..59e15a7 100644 --- a/flake8_import_style/tests/__init__.py +++ b/flake8_import_style/tests/__init__.py @@ -21,7 +21,7 @@ def test_run(self): self.assertEqual(list(i8.run()), [( 1, 0, - "I801 use 'import ...' instead of 'from ... import obj'", + "I801 use 'import obj' instead of 'from . import obj'", "I801", )]) @@ -30,7 +30,7 @@ def test_run(self): self.assertEqual(list(i8.run()), [( 1, 0, - "I801 use 'import ...' instead of 'from ... import obj'", + "I801 use 'import obj' instead of 'from .. import obj'", "I801", )]) @@ -39,7 +39,16 @@ def test_run(self): self.assertEqual(list(i8.run()), [( 1, 0, - "I801 use 'import mod' instead of 'from mod import obj'", + "I801 use 'import mod.obj' instead of 'from mod import obj'", + "I801", + )]) + + tree = ast.parse("from mod import obj1, obj2") + i8 = flake8_import_style.I8(tree) + self.assertEqual(list(i8.run()), [( + 1, + 0, + "I801 use 'import mod' instead of 'from mod import obj1, obj2'", "I801", )]) From 514d40479a6ebbe391d9f39d61bb0f548561d90d Mon Sep 17 00:00:00 2001 From: Eric Nielsen Date: Sat, 26 Mar 2022 23:18:58 -0500 Subject: [PATCH 2/2] Also support `from ..mod` relative import syntax --- flake8_import_style/__init__.py | 7 ++++--- flake8_import_style/tests/__init__.py | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/flake8_import_style/__init__.py b/flake8_import_style/__init__.py index b9ed778..ff3b8aa 100644 --- a/flake8_import_style/__init__.py +++ b/flake8_import_style/__init__.py @@ -22,21 +22,22 @@ def run(self): if isinstance(i, ast.ImportFrom): if i.module == "__future__": continue + module = ("." * i.level) + (i.module or "") names = ", ".join(i.name for i in i.names) if i.module is None: # Relative imports don't work with the `import <>` syntax. message = I801.format( stmt=names, - module="." * i.level, + module=module, names=names) elif len(i.names) == 1: message = I801.format( stmt="{0}.{1}".format(i.module, names), - module=i.module, + module=module, names=names) else: message = I801.format( stmt=i.module, - module=i.module, + module=module, names=names) yield (i.lineno, i.col_offset, message, "I801") diff --git a/flake8_import_style/tests/__init__.py b/flake8_import_style/tests/__init__.py index 59e15a7..a771b9a 100644 --- a/flake8_import_style/tests/__init__.py +++ b/flake8_import_style/tests/__init__.py @@ -34,6 +34,24 @@ def test_run(self): "I801", )]) + tree = ast.parse("from ..mod import obj") + i8 = flake8_import_style.I8(tree) + self.assertEqual(list(i8.run()), [( + 1, + 0, + "I801 use 'import mod.obj' instead of 'from ..mod import obj'", + "I801", + )]) + + tree = ast.parse("from .mod import obj1, obj2") + i8 = flake8_import_style.I8(tree) + self.assertEqual(list(i8.run()), [( + 1, + 0, + "I801 use 'import mod' instead of 'from .mod import obj1, obj2'", + "I801", + )]) + tree = ast.parse("from mod import obj") i8 = flake8_import_style.I8(tree) self.assertEqual(list(i8.run()), [(