Skip to content

Commit

Permalink
fix: check indented functions and classes (#293)
Browse files Browse the repository at this point in the history
Functions and classes inside an if block will now be checked

Signed-off-by: Stephen Whitlock <[email protected]>
  • Loading branch information
jshwi committed Apr 14, 2024
1 parent b0a3dee commit 15be6fc
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 37 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

[Unreleased](https://github.com/jshwi/docsig/compare/v0.46.0...HEAD)
------------------------------------------------------------------------
### Fixed
- check indented functions and classes

[0.46.0](https://github.com/jshwi/docsig/releases/tag/v0.46.0) - 2024-04-09
------------------------------------------------------------------------
Expand Down
108 changes: 71 additions & 37 deletions docsig/_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,55 +48,89 @@ def __init__( # pylint: disable=too-many-arguments
super().__init__()
self._name = node.name
self._path = f"{path}:" if path is not None else ""
overloads = []
overloads: list[str] = []
returns = None
self._parse_ast(
node,
directives,
path,
ignore_args,
ignore_kwargs,
check_class_constructor,
overloads,
returns,
)

def _parse_ast( # pylint: disable=protected-access,too-many-arguments
self,
node,
directives,
path,
ignore_args,
ignore_kwargs,
check_class_constructor,
overloads,
returns,
) -> None:
parent_comments, parent_disabled = directives.get(
node.lineno, ([], [])
)
for subnode in node.body:
comments, disabled = directives.get(subnode.lineno, ([], []))
comments.extend(parent_comments)
disabled.extend(parent_disabled)
if isinstance(subnode, _ast.FunctionDef):
func = Function(
subnode,
comments,
directives,
disabled,
path,
ignore_args,
ignore_kwargs,
check_class_constructor,
)
if func.isoverloaded:
overloads.append(func.name)
returns = func.signature.rettype
else:
if func.name in overloads:
subnode.returns = returns
# noinspection PyProtectedMember
func._signature._rettype = (
returns
if isinstance(returns, str)
else func._signature._get_rettype(returns)
)
# noinspection PyProtectedMember
func._signature._returns = (
str(func._signature._rettype) != "None"
if hasattr(node, "body"):
for subnode in node.body:
comments, disabled = directives.get(subnode.lineno, ([], []))
comments.extend(parent_comments)
disabled.extend(parent_disabled)
if isinstance(subnode, _ast.FunctionDef):
func = Function(
subnode,
comments,
directives,
disabled,
path,
ignore_args,
ignore_kwargs,
check_class_constructor,
)
if func.isoverloaded:
overloads.append(func.name)
returns = func.signature.rettype
else:
if func.name in overloads:
subnode.returns = returns
# noinspection PyProtectedMember
func._signature._rettype = (
returns
if isinstance(returns, str)
else func._signature._get_rettype(returns)
)
# noinspection PyProtectedMember
func._signature._returns = (
str(func._signature._rettype) != "None"
)

self.append(func)
elif isinstance(subnode, _ast.ClassDef):
self.append(
Parent(
subnode,
directives,
path,
ignore_args,
ignore_kwargs,
check_class_constructor,
)

self.append(func)
elif isinstance(subnode, _ast.ClassDef):
self.append(
Parent(
)
else:
self._parse_ast(
subnode,
directives,
path,
ignore_args,
ignore_kwargs,
check_class_constructor,
overloads,
returns,
)
)

@property
def path(self) -> str:
Expand Down
174 changes: 174 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8603,3 +8603,177 @@ def order(self, sig: _Param, doc: _Param) -> None:
@property
def expected(self) -> str:
return ""


@_templates.register
class _FFuncInIfStatementN(_BaseTemplate):
@property
def template(self) -> str:
return '''
def my_function(argument: int = 42) -> int:
"""
Function that prints a message and returns the argument + 1
Parameters
----------
argument : int, optional
The input argument, by default 42
Returns
-------
int
The input argument + 1
"""
print("Hello from a function")
print(argument)
return argument + 1
if True:
my_function(42)
def my_external_function(argument: int = 42) -> int:
print("Hello from an external function")
print(argument)
return argument + 42
'''

@property
def expected(self) -> str:
return """\
def my_external_function(✖argument) -> ✖int:
...
E113: function is missing a docstring (function-doc-missing)
"""


@_templates.register
class _FKlassInIfStatementN(_BaseTemplate):
@property
def template(self) -> str:
return '''
if True:
class Klass:
"""Class is OK."""
def my_external_function(self, argument: int = 42) -> int:
print("Hello from an external function")
print(argument)
return argument + 42
'''

@property
def expected(self) -> str:
return """\
def my_external_function(✖argument) -> ✖int:
...
E113: function is missing a docstring (function-doc-missing)
"""


@_templates.register
class _FFuncInIfInIfStatementN(_BaseTemplate):
@property
def template(self) -> str:
return """
if True:
if True:
def my_external_function(argument: int = 42) -> int:
print("Hello from an external function")
print(argument)
return argument + 42
"""

@property
def expected(self) -> str:
return """\
def my_external_function(✖argument) -> ✖int:
...
E113: function is missing a docstring (function-doc-missing)
"""


@_templates.register
class _FKlassNotMethodOkN(_BaseTemplate):
@property
def template(self) -> str:
return '''
class Klass:
def __init__(self, this) -> None:
self.this = this
def my_external_function(self, argument: int = 42) -> int:
"""This is a method.
:param argument: An int.
:return: An int.
"""
print("Hello from an external function")
print(argument)
return argument + 42
'''

@property
def expected(self) -> str:
return """\
class Klass:
...
def __init__(✖this) -> ✓None:
E114: class is missing a docstring (class-doc-missing)
"""


@_templates.register
class _FFuncInForLoopN(_BaseTemplate):
@property
def template(self) -> str:
return """
container = []
for argument in container:
def my_external_function(argument: int = 42) -> int:
print("Hello from an external function")
print(argument)
return argument + 42
"""

@property
def expected(self) -> str:
return """\
def my_external_function(✖argument) -> ✖int:
...
E113: function is missing a docstring (function-doc-missing)
"""


@_templates.register
class _FFuncInForLoopIfN(_BaseTemplate):
@property
def template(self) -> str:
return """
container = []
for argument in container:
if argument > 0:
def my_external_function(argument: int = 42) -> int:
print("Hello from an external function")
print(argument)
return argument + 42
"""

@property
def expected(self) -> str:
return """\
def my_external_function(✖argument) -> ✖int:
...
E113: function is missing a docstring (function-doc-missing)
"""
6 changes: 6 additions & 0 deletions whitelist.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
_FE112NI # unused class (tests/__init__.py)
_FE112S # unused class (tests/__init__.py)
_FE115NoSpaceS # unused class (tests/__init__.py)
_FFuncInForLoopIfN # unused class (tests/__init__.py)
_FFuncInForLoopN # unused class (tests/__init__.py)
_FFuncInIfInIfStatementN # unused class (tests/__init__.py)
_FFuncInIfStatementN # unused class (tests/__init__.py)
_FFuncPropG # unused class (tests/__init__.py)
_FFuncPropN # unused class (tests/__init__.py)
_FFuncPropNI # unused class (tests/__init__.py)
Expand All @@ -82,6 +86,8 @@
_FIncorrectDocS # unused class (tests/__init__.py)
_FIncorrectDocSumS # unused class (tests/__init__.py)
_FIssue36OffIndentN # unused class (tests/__init__.py)
_FKlassInIfStatementN # unused class (tests/__init__.py)
_FKlassNotMethodOkN # unused class (tests/__init__.py)
_FKwargsOutOfOrderG # unused class (tests/__init__.py)
_FKwargsOutOfOrderN # unused class (tests/__init__.py)
_FKwargsOutOfOrderNI # unused class (tests/__init__.py)
Expand Down

0 comments on commit 15be6fc

Please sign in to comment.