From 302bb78b6f38b07c1fa4a494b7a6dccf0d6b0e71 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 8 Jan 2023 18:53:31 +0100 Subject: [PATCH] Fix decorator parsing for async functions (#577) Co-authored-by: Sambhav Kothari Co-authored-by: Pierre Sassoulas --- docs/release_notes.rst | 7 ++++ src/pydocstyle/parser.py | 1 + src/tests/test_decorators.py | 15 +++++++++ src/tests/test_integration.py | 60 +++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 5ef059c1..f0f6f691 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -4,6 +4,13 @@ Release Notes **pydocstyle** version numbers follow the `Semantic Versioning `_ specification. +6.2.3 - January 8th, 2023 +--------------------------- + +Bug Fixes + +* Fix decorator parsing for async function. Resolves some false positives + with async functions and ``overload``. (#577) 6.2.2 - January 3rd, 2023 --------------------------- diff --git a/src/pydocstyle/parser.py b/src/pydocstyle/parser.py index 7165767a..cc768bbe 100644 --- a/src/pydocstyle/parser.py +++ b/src/pydocstyle/parser.py @@ -492,6 +492,7 @@ def parse_decorators(self): self.current.value, ) if self.current.kind == tk.NAME and self.current.value in [ + 'async', 'def', 'class', ]: diff --git a/src/tests/test_decorators.py b/src/tests/test_decorators.py index cc5e3f06..6fd050b8 100644 --- a/src/tests/test_decorators.py +++ b/src/tests/test_decorators.py @@ -130,6 +130,21 @@ def some_method(self): assert 'first_decorator' == decorators[0].name assert '' == decorators[0].arguments + def test_parse_async_function_decorator(self): + """Decorators for async functions are also accumulated.""" + code = textwrap.dedent("""\ + @first_decorator + async def some_method(self): + pass + """) + + module = checker.parse(io.StringIO(code), 'dummy.py') + decorators = module.children[0].decorators + + assert 1 == len(decorators) + assert 'first_decorator' == decorators[0].name + assert '' == decorators[0].arguments + def test_parse_method_nested_decorator(self): """Method decorators are accumulated for nested methods.""" code = textwrap.dedent("""\ diff --git a/src/tests/test_integration.py b/src/tests/test_integration.py index dbc6d315..2f2f57c6 100644 --- a/src/tests/test_integration.py +++ b/src/tests/test_integration.py @@ -621,6 +621,36 @@ def overloaded_func(a): assert 'D103' not in out +def test_overload_async_function(env): + """Async functions decorated with @overload trigger D418 error.""" + with env.open('example.py', 'wt') as example: + example.write(textwrap.dedent('''\ + from typing import overload + + + @overload + async def overloaded_func(a: int) -> str: + ... + + + @overload + async def overloaded_func(a: str) -> str: + """Foo bar documentation.""" + ... + + + async def overloaded_func(a): + """Foo bar documentation.""" + return str(a) + + ''')) + env.write_config(ignore="D100") + out, err, code = env.invoke() + assert code == 1 + assert 'D418' in out + assert 'D103' not in out + + def test_overload_method(env): """Methods decorated with @overload trigger D418 error.""" with env.open('example.py', 'wt') as example: @@ -714,6 +744,36 @@ def overloaded_func(a): assert code == 0 +def test_overload_async_function_valid(env): + """Valid case for overload decorated async functions. + + This shouldn't throw any errors. + """ + with env.open('example.py', 'wt') as example: + example.write(textwrap.dedent('''\ + from typing import overload + + + @overload + async def overloaded_func(a: int) -> str: + ... + + + @overload + async def overloaded_func(a: str) -> str: + ... + + + async def overloaded_func(a): + """Foo bar documentation.""" + return str(a) + + ''')) + env.write_config(ignore="D100") + out, err, code = env.invoke() + assert code == 0 + + def test_overload_nested_function(env): """Nested functions decorated with @overload trigger D418 error.""" with env.open('example.py', 'wt') as example: