diff --git a/pyflakes/checker.py b/pyflakes/checker.py index 135ad33c..6e98d749 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -693,6 +693,10 @@ def getNodeName(node): return node.name +def _is_dunder(name): + return name.startswith("__") and name.endswith("__") and len(name) > 4 + + TYPING_MODULES = frozenset(('typing', 'typing_extensions')) @@ -1026,7 +1030,9 @@ def checkDeadScopes(self): # Look for imported names that aren't used. for value in scope.values(): if isinstance(value, Importation): - used = value.used or value.name in all_names + used = (value.used or value.name in all_names + or isinstance(scope, ModuleScope) + and _is_dunder(value.name)) if not used: messg = messages.UnusedImport self.report(messg, value.source, str(value)) diff --git a/pyflakes/test/test_imports.py b/pyflakes/test/test_imports.py index d5be2693..335c00d6 100644 --- a/pyflakes/test/test_imports.py +++ b/pyflakes/test/test_imports.py @@ -154,6 +154,12 @@ def test_usedImport_relative(self): self.flakes('from .. import fu; assert fu') self.flakes('from ..bar import fu as baz; assert baz') + def test_implicitlyUsedDunderImport(self): + self.flakes('from foo import __author__') + self.flakes('from foo import version as __version__') + self.flakes('from .foo import __author__') + self.flakes('from .foo import version as __version__') + def test_redefinedWhileUnused(self): self.flakes('import fu; fu = 3', m.RedefinedWhileUnused) self.flakes('import fu; fu, bar = 3', m.RedefinedWhileUnused)