Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

Commit

Permalink
Fix decorator parsing for async functions (#577)
Browse files Browse the repository at this point in the history
Co-authored-by: Sambhav Kothari <sambhavs.email@gmail.com>
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
  • Loading branch information
3 people committed Jan 8, 2023
1 parent bd294bb commit 302bb78
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ Release Notes
**pydocstyle** version numbers follow the
`Semantic Versioning <http://semver.org/>`_ 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
---------------------------
Expand Down
1 change: 1 addition & 0 deletions src/pydocstyle/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]:
Expand Down
15 changes: 15 additions & 0 deletions src/tests/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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("""\
Expand Down
60 changes: 60 additions & 0 deletions src/tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 302bb78

Please sign in to comment.