From ba6b2f41f849a941e55f0756de0da9c3741a2f56 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 22 Feb 2019 09:58:59 -0800 Subject: [PATCH 1/2] Support @overload mixed with other decorators --- pyflakes/checker.py | 6 ++++-- pyflakes/test/test_type_annotations.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/pyflakes/checker.py b/pyflakes/checker.py index 4c88af29..8705781d 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -548,8 +548,10 @@ def is_typing_overload_decorator(node): return ( isinstance(value.source, ast.FunctionDef) and - len(value.source.decorator_list) == 1 and - is_typing_overload_decorator(value.source.decorator_list[0]) + any( + is_typing_overload_decorator(dec) + for dec in value.source.decorator_list + ) ) diff --git a/pyflakes/test/test_type_annotations.py b/pyflakes/test/test_type_annotations.py index 48635bbe..85e0cdc6 100644 --- a/pyflakes/test/test_type_annotations.py +++ b/pyflakes/test/test_type_annotations.py @@ -39,6 +39,25 @@ def g(s): return s """) + def test_overload_with_multiple_decorators(self): + self.flakes(""" + from typing import overload + dec = lambda f: f + + @dec + @overload + def f(x): # type: (int) -> int + pass + + @dec + @overload + def f(x): # type: (str) -> str + pass + + @dec + def f(x): return x + """) + def test_not_a_typing_overload(self): """regression test for @typing.overload detection bug in 2.1.0""" self.flakes(""" From e6cb793e5cee03cbcc890886c0357fa87840d8e4 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 22 Feb 2019 10:11:42 -0800 Subject: [PATCH 2/2] Detect @overload not just at module scope --- pyflakes/checker.py | 19 +++++++++++++------ pyflakes/test/test_type_annotations.py | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/pyflakes/checker.py b/pyflakes/checker.py index 8705781d..0e636c1f 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -530,14 +530,21 @@ def getNodeName(node): return node.name -def is_typing_overload(value, scope): +def is_typing_overload(value, scope_stack): + def name_is_typing_overload(name): # type: (str) -> bool + for scope in reversed(scope_stack): + if name in scope: + return ( + isinstance(scope[name], ImportationFrom) and + scope[name].fullName == 'typing.overload' + ) + else: + return False + def is_typing_overload_decorator(node): return ( ( - isinstance(node, ast.Name) and - node.id in scope and - isinstance(scope[node.id], ImportationFrom) and - scope[node.id].fullName == 'typing.overload' + isinstance(node, ast.Name) and name_is_typing_overload(node.id) ) or ( isinstance(node, ast.Attribute) and isinstance(node.value, ast.Name) and @@ -890,7 +897,7 @@ def addBinding(self, node, value): node, value.name, existing.source) elif not existing.used and value.redefines(existing): if value.name != '_' or isinstance(existing, Importation): - if not is_typing_overload(existing, self.scope): + if not is_typing_overload(existing, self.scopeStack): self.report(messages.RedefinedWhileUnused, node, value.name, existing.source) diff --git a/pyflakes/test/test_type_annotations.py b/pyflakes/test/test_type_annotations.py index 85e0cdc6..b8876cbe 100644 --- a/pyflakes/test/test_type_annotations.py +++ b/pyflakes/test/test_type_annotations.py @@ -58,6 +58,22 @@ def f(x): # type: (str) -> str def f(x): return x """) + def test_overload_in_class(self): + self.flakes(""" + from typing import overload + + class C: + @overload + def f(self, x): # type: (int) -> int + pass + + @overload + def f(self, x): # type: (str) -> str + pass + + def f(self, x): return x + """) + def test_not_a_typing_overload(self): """regression test for @typing.overload detection bug in 2.1.0""" self.flakes("""