From d2b4b995b1502f85f61c3e8b8768d55e2575026b Mon Sep 17 00:00:00 2001 From: "jsh9, PhD" <25124332+jsh9@users.noreply.github.com> Date: Thu, 8 Feb 2024 01:03:42 -0500 Subject: [PATCH] Include args with different type hints in DOC105 msg (#120) --- CHANGELOG.md | 11 +++++++++ pydoclint/utils/arg.py | 16 ++++++++++++++ pydoclint/utils/generic.py | 13 +++++++++++ pydoclint/utils/violation.py | 2 +- pydoclint/visitor.py | 15 +++++++++++-- setup.cfg | 2 +- tests/test_main.py | 43 ++++++++++++++++++++---------------- 7 files changed, 79 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3ac128..c8ef39b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Change Log +## [0.4.0] - 2024-02-08 + +- Changed + + - Improved the violation message of DOC105: the arguments with inconsistent + type hints are now shown in the violation message to make violation + correction much easier + +- Full diff + - https://github.com/jsh9/pydoclint/compare/0.3.10...0.4.0 + ## [0.3.10] - 2024-02-07 - Added diff --git a/pydoclint/utils/arg.py b/pydoclint/utils/arg.py index 7ce6b40..d5e632a 100644 --- a/pydoclint/utils/arg.py +++ b/pydoclint/utils/arg.py @@ -5,6 +5,7 @@ from pydoclint.utils.annotation import unparseAnnotation from pydoclint.utils.generic import stripQuotes +from pydoclint.utils.internal_error import InternalError class Arg: @@ -232,6 +233,21 @@ def equals( return verdict # noqa: R504 + def findArgsWithDifferentTypeHints(self, other: 'ArgList') -> List[Arg]: + """Find args with unmatched type hints.""" + if not self.equals(other, checkTypeHint=False, orderMatters=False): + msg = 'These 2 arg lists do not have the same arg names' + raise InternalError(msg) + + result: List[Arg] = [] + for selfArg in self.infoList: + selfArgTypeHint: str = selfArg.typeHint + otherArgTypeHint: str = other.lookup[selfArg.name] + if selfArgTypeHint != otherArgTypeHint: + result.append(selfArg) + + return result + def subtract(self, other: 'ArgList', checkTypeHint=True) -> Set[Arg]: """Find the args that are in this object but not in `other`.""" if checkTypeHint: diff --git a/pydoclint/utils/generic.py b/pydoclint/utils/generic.py index afb1537..b5e28bb 100644 --- a/pydoclint/utils/generic.py +++ b/pydoclint/utils/generic.py @@ -5,6 +5,7 @@ from pydoclint.utils.astTypes import ClassOrFunctionDef, FuncOrAsyncFuncDef from pydoclint.utils.method_type import MethodType +from pydoclint.utils.violation import Violation def collectFuncArgs(node: FuncOrAsyncFuncDef) -> List[ast.arg]: @@ -175,3 +176,15 @@ def _replacer(match: Match[str]) -> str: # Otherwise, remove all quotes return match.group(0).replace('"', '').replace("'", '') + + +def appendArgsToCheckToV105( + *, + original_v105: Violation, + funcArgs: 'ArgList', # noqa: F821 + docArgs: 'ArgList', # noqa: F821 +) -> Violation: + """Append the arg names to check to the error message of v105""" + argsToCheck: List['Arg'] = funcArgs.findArgsWithDifferentTypeHints(docArgs) # noqa: F821 + argNames: str = ', '.join(_.name for _ in argsToCheck) + return original_v105.appendMoreMsg(moreMsg=argNames) diff --git a/pydoclint/utils/violation.py b/pydoclint/utils/violation.py index b95a703..bc36b2c 100644 --- a/pydoclint/utils/violation.py +++ b/pydoclint/utils/violation.py @@ -14,7 +14,7 @@ ' (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ).' ), 104: 'Arguments are the same in the docstring and the function signature, but are in a different order.', - 105: 'Argument names match, but type hints do not match', + 105: 'Argument names match, but type hints in these args do not match:', 106: 'The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature', 107: 'The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints', 108: 'The option `--arg-type-hints-in-signature` is `False` but there are argument type hints in the signature', diff --git a/pydoclint/visitor.py b/pydoclint/visitor.py index 8bf1939..964541d 100644 --- a/pydoclint/visitor.py +++ b/pydoclint/visitor.py @@ -6,6 +6,7 @@ from pydoclint.utils.astTypes import FuncOrAsyncFuncDef from pydoclint.utils.doc import Doc from pydoclint.utils.generic import ( + appendArgsToCheckToV105, collectFuncArgs, detectMethodType, generateMsgPrefix, @@ -425,14 +426,24 @@ def checkArguments( # noqa: C901 self.argTypeHintsInSignature and self.argTypeHintsInDocstring ): - violations.append(v105) + v105_new = appendArgsToCheckToV105( + original_v105=v105, + funcArgs=funcArgs, + docArgs=docArgs, + ) + violations.append(v105_new) elif docArgs.equals( funcArgs, checkTypeHint=False, orderMatters=False, ): + v105_new = appendArgsToCheckToV105( + original_v105=v105, + funcArgs=funcArgs, + docArgs=docArgs, + ) violations.append(v104) - violations.append(v105) + violations.append(v105_new) else: argsInFuncNotInDoc: Set[Arg] = funcArgs.subtract( docArgs, diff --git a/setup.cfg b/setup.cfg index 586c08e..012f442 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pydoclint -version = 0.3.10 +version = 0.4.0 description = A Python docstring linter that checks arguments, returns, yields, and raises sections long_description = file: README.md long_description_content_type = text/markdown diff --git a/tests/test_main.py b/tests/test_main.py index e4245c7..dfe091c 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -47,10 +47,12 @@ def pythonVersionBelow310(): 'function signature: [arg3: Optional[Union[float, int, str]]].', 'DOC104: Method `MyClass.func4`: Arguments are the same in the docstring and ' 'the function signature, but are in a different order. ', - 'DOC105: Method `MyClass.func5`: Argument names match, but type hints do not match ', + 'DOC105: Method `MyClass.func5`: Argument names match, but type hints in these args ' + 'do not match: arg1, arg2', 'DOC104: Method `MyClass.func6`: Arguments are the same in the docstring and ' 'the function signature, but are in a different order. ', - 'DOC105: Method `MyClass.func6`: Argument names match, but type hints do not match ', + 'DOC105: Method `MyClass.func6`: Argument names match, but type hints in these args ' + 'do not match: arg1, arg2', 'DOC101: Function `func72`: Docstring contains fewer arguments than in ' 'function signature. ', 'DOC109: Function `func72`: The option `--arg-type-hints-in-docstring` is `True` ' @@ -90,10 +92,10 @@ def pythonVersionBelow310(): 'function arguments. (Or could be other formatting issues: ' 'https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the docstring but not in the ' 'function signature: [arg3: Optional[Union[float, int, str]]].', - 'DOC105: Method `MyClass.func5`: Argument names match, but type hints do not ' - 'match ', - 'DOC105: Method `MyClass.func6`: Argument names match, but type hints do not ' - 'match ', + 'DOC105: Method `MyClass.func5`: Argument names match, but type hints in ' + 'these args do not match: arg1, arg2', + 'DOC105: Method `MyClass.func6`: Argument names match, but type hints in ' + 'these args do not match: arg1, arg2', 'DOC101: Function `func72`: Docstring contains fewer arguments than in ' 'function signature. ', 'DOC109: Function `func72`: The option `--arg-type-hints-in-docstring` is `True` ' @@ -426,8 +428,8 @@ def testInit(style: str) -> None: 'with the docstring of the class ', 'DOC302: Class `B`: The class docstring does not need a "Returns" section, ' 'because __init__() cannot return anything ', - 'DOC105: Method `C.__init__`: Argument names match, but type hints do not ' - 'match ', + 'DOC105: Method `C.__init__`: Argument names match, but type hints in these ' + 'args do not match: arg2', 'DOC302: Class `C`: The class docstring does not need a "Returns" section, ' 'because __init__() cannot return anything ', 'DOC103: Method `D.__init__`: Docstring arguments are different from function ' @@ -1014,22 +1016,22 @@ def testTypeHintChecking( '`True` but there are no type hints in the docstring arg list ', 'DOC110: Method `MyClass.func2`: The option `--arg-type-hints-in-docstring` is ' '`True` but not all args in the docstring arg list have type hints ', - 'DOC105: Method `MyClass.func2`: Argument names match, but type hints do not ' - 'match ', + 'DOC105: Method `MyClass.func2`: Argument names match, but type hints in ' + 'these args do not match: arg1, arg2', 'DOC106: Method `MyClass.func3`: The option `--arg-type-hints-in-signature` is ' '`True` but there are no argument type hints in the signature ', 'DOC107: Method `MyClass.func3`: The option `--arg-type-hints-in-signature` is ' '`True` but not all args in the signature have type hints ', - 'DOC105: Method `MyClass.func3`: Argument names match, but type hints do not ' - 'match ', + 'DOC105: Method `MyClass.func3`: Argument names match, but type hints in ' + 'these args do not match: arg1, arg2', 'DOC107: Method `MyClass.func5`: The option `--arg-type-hints-in-signature` is ' '`True` but not all args in the signature have type hints ', 'DOC110: Method `MyClass.func5`: The option `--arg-type-hints-in-docstring` is ' '`True` but not all args in the docstring arg list have type hints ', - 'DOC105: Method `MyClass.func5`: Argument names match, but type hints do not ' - 'match ', - 'DOC105: Method `MyClass.func6`: Argument names match, but type hints do not ' - 'match ', + 'DOC105: Method `MyClass.func5`: Argument names match, but type hints in ' + 'these args do not match: arg1, arg2', + 'DOC105: Method `MyClass.func6`: Argument names match, but type hints in ' + 'these args do not match: arg1', 'DOC107: Method `MyClass.func7`: The option `--arg-type-hints-in-signature` is ' '`True` but not all args in the signature have type hints ', 'DOC110: Method `MyClass.func7`: The option `--arg-type-hints-in-docstring` is ' @@ -1066,17 +1068,20 @@ def testNonAscii() -> None: '`True` but there are no argument type hints in the signature ', 'DOC107: Function `func1`: The option `--arg-type-hints-in-signature` is ' '`True` but not all args in the signature have type hints ', - 'DOC105: Function `func1`: Argument names match, but type hints do not match ', + 'DOC105: Function `func1`: Argument names match, but type hints in these args ' + 'do not match: a', 'DOC106: Function `func2`: The option `--arg-type-hints-in-signature` is ' '`True` but there are no argument type hints in the signature ', 'DOC107: Function `func2`: The option `--arg-type-hints-in-signature` is ' '`True` but not all args in the signature have type hints ', - 'DOC105: Function `func2`: Argument names match, but type hints do not match ', + 'DOC105: Function `func2`: Argument names match, but type hints in these args ' + 'do not match: a', 'DOC106: Function `func3`: The option `--arg-type-hints-in-signature` is ' '`True` but there are no argument type hints in the signature ', 'DOC107: Function `func3`: The option `--arg-type-hints-in-signature` is ' '`True` but not all args in the signature have type hints ', - 'DOC105: Function `func3`: Argument names match, but type hints do not match ', + 'DOC105: Function `func3`: Argument names match, but type hints in these args ' + 'do not match: a', ], ), (