diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 5ef059c..f0f6f69 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 7165767..cc768bb 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 cc5e3f0..6fd050b 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 dbc6d31..2f2f57c 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: