diff --git a/CHANGELOG.md b/CHANGELOG.md index a75a2e3..f50b6bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - Fixed - Fixed a bug where assigning a value to an attribute caused pydoclint to crash +- Changed + - Renamed function `unparseAnnotation()` into `unparseNode()` ## [0.5.9] - 2024-09-29 diff --git a/pydoclint/utils/arg.py b/pydoclint/utils/arg.py index a021e07..02ca84a 100644 --- a/pydoclint/utils/arg.py +++ b/pydoclint/utils/arg.py @@ -3,9 +3,9 @@ from docstring_parser.common import DocstringAttr, DocstringParam -from pydoclint.utils.annotation import unparseAnnotation from pydoclint.utils.generic import stripQuotes from pydoclint.utils.internal_error import InternalError +from pydoclint.utils.unparser_custom import unparseName class Arg: @@ -85,15 +85,15 @@ def fromDocstringAttr(cls, attr: DocstringAttr) -> 'Arg': def fromAstArg(cls, astArg: ast.arg) -> 'Arg': """Construct an Arg object from a Python AST argument object""" anno = astArg.annotation - typeHint: str = '' if anno is None else unparseAnnotation(anno) + typeHint: str = '' if anno is None else unparseName(anno) return Arg(name=astArg.arg, typeHint=typeHint) @classmethod def fromAstAnnAssign(cls, astAnnAssign: ast.AnnAssign) -> 'Arg': """Construct an Arg object from a Python ast.AnnAssign object""" return Arg( - name=unparseAnnotation(astAnnAssign.target), - typeHint=unparseAnnotation(astAnnAssign.annotation), + name=unparseName(astAnnAssign.target), + typeHint=unparseName(astAnnAssign.annotation), ) @classmethod @@ -113,12 +113,12 @@ def _typeHintsEq(cls, hint1: str, hint2: str) -> bool: # >>> "ghi", # >>> ] try: - hint1_: str = unparseAnnotation(ast.parse(stripQuotes(hint1))) + hint1_: str = unparseName(ast.parse(stripQuotes(hint1))) except SyntaxError: hint1_ = hint1 try: - hint2_: str = unparseAnnotation(ast.parse(stripQuotes(hint2))) + hint2_: str = unparseName(ast.parse(stripQuotes(hint2))) except SyntaxError: hint2_ = hint2 @@ -221,9 +221,7 @@ def fromAstAssign(cls, astAssign: ast.Assign) -> 'ArgList': elif isinstance(target, ast.Name): # such as `a = 1` or `a = b = 2` infoList.append(Arg(name=target.id, typeHint='')) elif isinstance(target, ast.Attribute): # e.g., uvw.xyz = 1 - infoList.append( - Arg(name=unparseAnnotation(target), typeHint='') - ) + infoList.append(Arg(name=unparseName(target), typeHint='')) else: raise InternalError( f'astAssign.targets[{i}] is of type {type(target)}' diff --git a/pydoclint/utils/return_anno.py b/pydoclint/utils/return_anno.py index 994b328..e27ccd1 100644 --- a/pydoclint/utils/return_anno.py +++ b/pydoclint/utils/return_anno.py @@ -2,9 +2,9 @@ import json from typing import List, Optional -from pydoclint.utils.annotation import unparseAnnotation from pydoclint.utils.generic import stripQuotes from pydoclint.utils.internal_error import InternalError +from pydoclint.utils.unparser_custom import unparseName class ReturnAnnotation: @@ -57,7 +57,7 @@ def decompose(self) -> List[str]: if isinstance(parsedBody0.value, ast.Tuple): # like Tuple[int, str] elts: List = parsedBody0.value.elts - return [unparseAnnotation(_) for _ in elts] + return [unparseName(_) for _ in elts] raise InternalError('decompose(): This should not have happened') else: diff --git a/pydoclint/utils/return_yield_raise.py b/pydoclint/utils/return_yield_raise.py index 4a316d9..4786a76 100644 --- a/pydoclint/utils/return_yield_raise.py +++ b/pydoclint/utils/return_yield_raise.py @@ -2,9 +2,9 @@ from typing import Callable, Dict, Generator, List, Optional, Tuple, Type from pydoclint.utils import walk -from pydoclint.utils.annotation import unparseAnnotation from pydoclint.utils.astTypes import BlockType, FuncOrAsyncFuncDef from pydoclint.utils.generic import getFullAttributeName, stringStartsWith +from pydoclint.utils.unparser_custom import unparseName ReturnType = Type[ast.Return] ExprType = Type[ast.Expr] @@ -32,7 +32,7 @@ def isReturnAnnotationNoReturn(node: FuncOrAsyncFuncDef) -> bool: if node.returns is None: return False - returnAnnotation: str = unparseAnnotation(node.returns) + returnAnnotation: str = unparseName(node.returns) return returnAnnotation == 'NoReturn' @@ -41,7 +41,7 @@ def hasGeneratorAsReturnAnnotation(node: FuncOrAsyncFuncDef) -> bool: if node.returns is None: return False - returnAnno: str = unparseAnnotation(node.returns) + returnAnno: str = unparseName(node.returns) return returnAnno in {'Generator', 'AsyncGenerator'} or stringStartsWith( returnAnno, ('Generator[', 'AsyncGenerator[') ) @@ -52,7 +52,7 @@ def hasIteratorOrIterableAsReturnAnnotation(node: FuncOrAsyncFuncDef) -> bool: if node.returns is None: return False - returnAnnotation: str = unparseAnnotation(node.returns) + returnAnnotation: str = unparseName(node.returns) return returnAnnotation in { 'Iterator', 'Iterable', diff --git a/pydoclint/utils/annotation.py b/pydoclint/utils/unparser_custom.py similarity index 98% rename from pydoclint/utils/annotation.py rename to pydoclint/utils/unparser_custom.py index 261691b..50d5eaf 100644 --- a/pydoclint/utils/annotation.py +++ b/pydoclint/utils/unparser_custom.py @@ -38,7 +38,7 @@ def unparse(tree: ast.AST) -> str: return fp.getvalue() -def unparseAnnotation( +def unparseName( node: Union[AnnotationType, ast.Module, None], ) -> Optional[str]: """Parse type annotations from argument list or return annotation.""" diff --git a/pydoclint/utils/visitor_helper.py b/pydoclint/utils/visitor_helper.py index 0ea5f38..f9660f8 100644 --- a/pydoclint/utils/visitor_helper.py +++ b/pydoclint/utils/visitor_helper.py @@ -3,7 +3,6 @@ import sys from typing import List, Optional, Set -from pydoclint.utils.annotation import unparseAnnotation from pydoclint.utils.arg import Arg, ArgList from pydoclint.utils.doc import Doc from pydoclint.utils.generic import ( @@ -16,6 +15,7 @@ from pydoclint.utils.return_anno import ReturnAnnotation from pydoclint.utils.return_arg import ReturnArg from pydoclint.utils.special_methods import checkIsPropertyMethod +from pydoclint.utils.unparser_custom import unparseName from pydoclint.utils.violation import Violation from pydoclint.utils.yield_arg import YieldArg @@ -170,7 +170,7 @@ def extractClassAttributesFromNode( atl.append( Arg( name=itm.name, - typeHint=unparseAnnotation(itm.returns), + typeHint=unparseName(itm.returns), ) ) @@ -482,15 +482,15 @@ def extractYieldTypeFromGeneratorOrIteratorAnnotation( if hasGeneratorAsReturnAnnotation: if sys.version_info >= (3, 9): - yieldType = unparseAnnotation( + yieldType = unparseName( ast.parse(returnAnnoText).body[0].value.slice.elts[0] ) else: - yieldType = unparseAnnotation( + yieldType = unparseName( ast.parse(returnAnnoText).body[0].value.slice.value.elts[0] ) elif hasIteratorOrIterableAsReturnAnnotation: - yieldType = unparseAnnotation( + yieldType = unparseName( ast.parse(returnAnnoText).body[0].value.slice ) else: @@ -510,11 +510,11 @@ def extractReturnTypeFromGenerator(returnAnnoText: str) -> str: # https://docs.python.org/3/library/typing.html#typing.Generator returnType: str if sys.version_info >= (3, 9): - returnType = unparseAnnotation( + returnType = unparseName( ast.parse(returnAnnoText).body[0].value.slice.elts[-1] ) else: - returnType = unparseAnnotation( + returnType = unparseName( ast.parse(returnAnnoText).body[0].value.slice.value.elts[-1] ) except Exception: diff --git a/pydoclint/visitor.py b/pydoclint/visitor.py index ccfc5a0..24bfdbd 100644 --- a/pydoclint/visitor.py +++ b/pydoclint/visitor.py @@ -1,7 +1,6 @@ import ast from typing import List, Optional -from pydoclint.utils.annotation import unparseAnnotation from pydoclint.utils.arg import Arg, ArgList from pydoclint.utils.astTypes import FuncOrAsyncFuncDef from pydoclint.utils.doc import Doc @@ -31,6 +30,7 @@ checkIsAbstractMethod, checkIsPropertyMethod, ) +from pydoclint.utils.unparser_custom import unparseName from pydoclint.utils.violation import Violation from pydoclint.utils.visitor_helper import ( addMismatchedRaisesExceptionViolation, @@ -518,7 +518,7 @@ def checkReturns( # noqa: C901 if self.checkReturnTypes: if hasReturnAnno: - returnAnno = ReturnAnnotation(unparseAnnotation(node.returns)) + returnAnno = ReturnAnnotation(unparseName(node.returns)) else: returnAnno = ReturnAnnotation(annotation=None) @@ -611,7 +611,7 @@ def checkYields( # noqa: C901 noGenNorIterAsRetAnno = not hasGenAsRetAnno and not hasIterAsRetAnno if hasGenAsRetAnno or hasIterAsRetAnno: - returnAnno = ReturnAnnotation(unparseAnnotation(node.returns)) + returnAnno = ReturnAnnotation(unparseName(node.returns)) else: # We don't check other return annotations here, because they # are checked above, in `checkReturns()`. @@ -718,7 +718,7 @@ def my_function(num: int) -> Generator[int, None, str]: onlyHasYieldStmt: bool = hasYieldStmt and not hasReturnStmt hasReturnAnno: bool = hasReturnAnnotation(node) - returnAnno = ReturnAnnotation(unparseAnnotation(node.returns)) + returnAnno = ReturnAnnotation(unparseName(node.returns)) returnSec: List[ReturnArg] = doc.returnSection # Check the return section in the docstring @@ -770,7 +770,7 @@ def my_function(num: int) -> Generator[int, None, str]: violations.append(v402) else: if self.checkYieldTypes: - returnAnno = ReturnAnnotation(unparseAnnotation(node.returns)) + returnAnno = ReturnAnnotation(unparseName(node.returns)) yieldSec: List[YieldArg] = doc.yieldSection if hasGenAsRetAnno or hasIterAsRetAnno: diff --git a/tests/utils/test_annotation.py b/tests/utils/test_annotation.py index 6e2d367..3e34544 100644 --- a/tests/utils/test_annotation.py +++ b/tests/utils/test_annotation.py @@ -4,7 +4,7 @@ import pytest -from pydoclint.utils.annotation import unparseAnnotation +from pydoclint.utils.unparser_custom import unparseName def testUnparseAnnotationInArguments() -> None: @@ -122,7 +122,7 @@ def foo( def _getArgTypeHints(node: ast.FunctionDef) -> Dict[str, str]: hints = {} for arg_ in node.args.args: - hints[arg_.arg] = unparseAnnotation(arg_.annotation) + hints[arg_.arg] = unparseName(arg_.annotation) return hints @@ -150,6 +150,6 @@ def testParseReturnAnnotation(src: str, expectedAnnotation: str): returnAnnotation: Optional[str] = None for node in tree.body: if isinstance(node, ast.FunctionDef): - returnAnnotation: str = unparseAnnotation(node.returns) + returnAnnotation: str = unparseName(node.returns) assert returnAnnotation == expectedAnnotation