diff --git a/CHANGELOG.md b/CHANGELOG.md index 2db9b7b..803433f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## Unreleased + +- Changed + - Replaced the `--check-type-hint` option with two new options: + `--type-hints-in-docstring` and `--type-hints-in-signature` + ## [0.0.8] - 2023-06-06 - Added diff --git a/README.md b/README.md index 967ad50..53fc243 100644 --- a/README.md +++ b/README.md @@ -248,22 +248,26 @@ or flake8 --style=google ``` -### 4.4. `--check-type-hint` (shortform: `-th`, default: `True`) - -If `True`, the type hints in the docstring and in the Python code need to -exactly match. - -To turn this option on/off, do this: - -``` -pydoclint --check-type-hint=False -``` - -or - -``` -flake8 --check-type-hint=False -``` +### 4.4. `--type-hints-in-docstring` and `--type-hints-in-signature` + +- `--type-hints-in-docstring` + - Shortform: `-thd` + - Default: `True` + - Meaning: + - If `True`, there need to be type hints in the argument list of a + docstring + - If `False`, there cannot be any type hints in the argument list of a + docstring +- `--type-hints-in-signature` + - Shortform: `-ths` + - Default: `True` + - Meaning: + - If `True`, there need to be type hints in the function/method signature + - If `False`, there cannot be any type hints in the function/method + signature + +Note: if users choose `True` for both options, the type hints in the signature +and in the docstring need to match, otherwise there will be a style violation. ### 4.5. `--check-arg-order` (shortform: `-ao`, default: `True`) diff --git a/pydoclint/flake8_entry.py b/pydoclint/flake8_entry.py index 769a362..e40bdad 100644 --- a/pydoclint/flake8_entry.py +++ b/pydoclint/flake8_entry.py @@ -24,12 +24,20 @@ def add_options(cls, parser): # noqa: D102 help='Which style of docstring is your code base using', ) parser.add_option( - '-th', - '--check-type-hint', + '-ths', + '--type-hints-in-signature', action='store', default='True', parse_from_config=True, - help='Whether to check type hints in the docstring', + help='Whether to require type hints in function signatures', + ) + parser.add_option( + '-thd', + '--type-hints-in-docstring', + action='store', + default='True', + parse_from_config=True, + help='Whether to require type hints in the argument list in docstrings', ) parser.add_option( '-ao', @@ -81,7 +89,8 @@ def add_options(cls, parser): # noqa: D102 @classmethod def parse_options(cls, options): # noqa: D102 - cls.check_type_hint = options.check_type_hint + cls.type_hints_in_signature = options.type_hints_in_signature + cls.type_hints_in_docstring = options.type_hints_in_docstring cls.check_arg_order = options.check_arg_order cls.skip_checking_short_docstrings = ( options.skip_checking_short_docstrings @@ -95,7 +104,14 @@ def parse_options(cls, options): # noqa: D102 def run(self) -> Generator[Tuple[int, int, str, Any], None, None]: """Run the linter and yield the violation information""" - checkTypeHint = self._bool('--check-type-hint', self.check_type_hint) + typeHintsInSignature = self._bool( + '--type-hints-in-signature', + self.type_hints_in_signature, + ) + typeHintsInDocstring = self._bool( + '--type-hints-in-docstring', + self.type_hints_in_docstring, + ) checkArgOrder = self._bool('--check-arg-order', self.check_arg_order) skipCheckingShortDocstrings = self._bool( '--skip-checking-short-docstrings', @@ -120,7 +136,8 @@ def run(self) -> Generator[Tuple[int, int, str, Any], None, None]: ) v = Visitor( - checkTypeHint=checkTypeHint, + typeHintsInSignature=typeHintsInSignature, + typeHintsInDocstring=typeHintsInDocstring, checkArgOrder=checkArgOrder, skipCheckingShortDocstrings=skipCheckingShortDocstrings, skipCheckingRaises=skipCheckingRaises, diff --git a/pydoclint/main.py b/pydoclint/main.py index 8b73c79..880ea7a 100644 --- a/pydoclint/main.py +++ b/pydoclint/main.py @@ -68,12 +68,20 @@ def validateStyleValue( help='', ) @click.option( - '-th', - '--check-type-hint', + '-ths', + '--type-hints-in-signature', type=bool, show_default=True, default=True, - help='Whether to check type hints in docstrings', + help='Whether to require type hints in function signatures', +) +@click.option( + '-thd', + '--type-hints-in-docstring', + type=bool, + show_default=True, + default=True, + help='Whether to require type hints in the argument list in docstrings', ) @click.option( '-ao', @@ -158,13 +166,14 @@ def main( # noqa: C901 style: str, src: Optional[str], paths: Tuple[str, ...], - check_type_hint: bool, + type_hints_in_signature: bool, + type_hints_in_docstring: bool, check_arg_order: bool, skip_checking_short_docstrings: bool, skip_checking_raises: bool, allow_init_docstring: bool, require_return_section_when_returning_none: bool, - config: Optional[str], + config: Optional[str], # don't remove it b/c it's required by `click` ) -> None: """Command-line entry point of pydoclint""" ctx.ensure_object(dict) @@ -189,7 +198,8 @@ def main( # noqa: C901 exclude=exclude, style=style, paths=paths, - checkTypeHint=check_type_hint, + typeHintsInSignature=type_hints_in_signature, + typeHintsInDocstring=type_hints_in_docstring, checkArgOrder=check_arg_order, skipCheckingShortDocstrings=skip_checking_short_docstrings, skipCheckingRaises=skip_checking_raises, @@ -243,7 +253,8 @@ def main( # noqa: C901 def _checkPaths( paths: Tuple[str, ...], style: str = 'numpy', - checkTypeHint: bool = True, + typeHintsInSignature: bool = True, + typeHintsInDocstring: bool = True, checkArgOrder: bool = True, skipCheckingShortDocstrings: bool = True, skipCheckingRaises: bool = False, @@ -283,7 +294,8 @@ def _checkPaths( violationsInThisFile: List[Violation] = _checkFile( filename, style=style, - checkTypeHint=checkTypeHint, + typeHintsInSignature=typeHintsInSignature, + typeHintsInDocstring=typeHintsInDocstring, checkArgOrder=checkArgOrder, skipCheckingShortDocstrings=skipCheckingShortDocstrings, skipCheckingRaises=skipCheckingRaises, @@ -298,7 +310,8 @@ def _checkPaths( def _checkFile( filename: Path, style: str = 'numpy', - checkTypeHint: bool = True, + typeHintsInSignature: bool = True, + typeHintsInDocstring: bool = True, checkArgOrder: bool = True, skipCheckingShortDocstrings: bool = True, skipCheckingRaises: bool = False, @@ -311,7 +324,8 @@ def _checkFile( tree: ast.Module = ast.parse(src) visitor = Visitor( style=style, - checkTypeHint=checkTypeHint, + typeHintsInSignature=typeHintsInSignature, + typeHintsInDocstring=typeHintsInDocstring, checkArgOrder=checkArgOrder, skipCheckingShortDocstrings=skipCheckingShortDocstrings, skipCheckingRaises=skipCheckingRaises, diff --git a/pydoclint/utils/arg.py b/pydoclint/utils/arg.py index c4f988e..37c3a64 100644 --- a/pydoclint/utils/arg.py +++ b/pydoclint/utils/arg.py @@ -56,6 +56,18 @@ def nameEquals(self, other: 'Arg') -> bool: """More lenient equality: only compare names""" return self.name == other.name + def hasTypeHint(self) -> bool: + """Check whether this arg has type hint""" + return self.typeHint != '' + + def isStarArg(self) -> bool: + """Check whether this arg is a star arg (such as *args, **kwargs)""" + return self.name.startswith('*') + + def notStarArg(self) -> bool: + """Check whether this arg is not a star arg (*args, **kwargs)""" + return not self.isStarArg() + @classmethod def fromNumpydocParam(cls, param: Parameter) -> 'Arg': """Construct an Arg object from a Numpydoc Parameter object""" @@ -205,3 +217,25 @@ def equals( def subtract(self, other: 'ArgList') -> Set[Arg]: """Find the args that are in this object but not in `other`.""" return set(self.infoList) - set(other.infoList) + + def noTypeHints(self) -> bool: + """Check whether none of the args have type hints""" + return not self.hasTypeHintInAnyArg() + + def hasTypeHintInAnyArg(self) -> bool: + """ + Check whether any arg has a type hint. + + Start arguments (such as `*args` or `**kwargs`) are excluded because + they don't need to have type hints. + """ + return any(_.hasTypeHint() for _ in self.infoList if _.notStarArg()) + + def hasTypeHintInAllArgs(self) -> bool: + """ + Check whether all args have a type hint. + + Start arguments (such as `*args` or `**kwargs`) are excluded because + they don't need to have type hints. + """ + return all(_.hasTypeHint() for _ in self.infoList if _.notStarArg()) diff --git a/pydoclint/utils/violation.py b/pydoclint/utils/violation.py index 67c041f..e7714bd 100644 --- a/pydoclint/utils/violation.py +++ b/pydoclint/utils/violation.py @@ -14,6 +14,12 @@ ), 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', + 106: 'The option `--type-hints-in-signature` is `True` but there are no type hints in the signature', + 107: 'The option `--type-hints-in-signature` is `True` but not all args in the signature have type hints', + 108: 'The option `--type-hints-in-signature` is `False` but there are type hints in the signature', + 109: 'The option `--type-hints-in-docstring` is `True` but there are no type hints in the docstring arg list', + 110: 'The option `--type-hints-in-docstring` is `True` but not all args in the docstring arg list have type hints', + 111: 'The option `--type-hints-in-docstring` is `False` but there are type hints in the docstring arg list', 201: 'does not have a return section in docstring', 202: 'has a return section in docstring, but there are no return statements or annotations', diff --git a/pydoclint/visitor.py b/pydoclint/visitor.py index 231ffae..9a05683 100644 --- a/pydoclint/visitor.py +++ b/pydoclint/visitor.py @@ -31,7 +31,8 @@ class Visitor(ast.NodeVisitor): def __init__( self, style: str = 'numpy', - checkTypeHint: bool = True, + typeHintsInSignature: bool = True, + typeHintsInDocstring: bool = True, checkArgOrder: bool = True, skipCheckingShortDocstrings: bool = True, skipCheckingRaises: bool = False, @@ -39,7 +40,8 @@ def __init__( requireReturnSectionWhenReturningNone: bool = False, ) -> None: self.style: str = style - self.checkTypeHint: bool = checkTypeHint + self.typeHintsInSignature: bool = typeHintsInSignature + self.typeHintsInDocstring: bool = typeHintsInDocstring self.checkArgOrder: bool = checkArgOrder self.skipCheckingShortDocstrings: bool = skipCheckingShortDocstrings self.skipCheckingRaises: bool = skipCheckingRaises @@ -315,9 +317,15 @@ def checkArguments( # noqa: C901 v102 = Violation(code=102, line=lineNum, msgPrefix=msgPrefix) v104 = Violation(code=104, line=lineNum, msgPrefix=msgPrefix) v105 = Violation(code=105, line=lineNum, msgPrefix=msgPrefix) + v106 = Violation(code=106, line=lineNum, msgPrefix=msgPrefix) + v107 = Violation(code=107, line=lineNum, msgPrefix=msgPrefix) + v108 = Violation(code=108, line=lineNum, msgPrefix=msgPrefix) + v109 = Violation(code=109, line=lineNum, msgPrefix=msgPrefix) + v110 = Violation(code=110, line=lineNum, msgPrefix=msgPrefix) + v111 = Violation(code=111, line=lineNum, msgPrefix=msgPrefix) - docArgs = doc.argList - funcArgs = ArgList([Arg.fromAstArg(_) for _ in astArgList]) + docArgs: ArgList = doc.argList + funcArgs: ArgList = ArgList([Arg.fromAstArg(_) for _ in astArgList]) if docArgs.length == 0 and funcArgs.length == 0: return [] @@ -329,14 +337,32 @@ def checkArguments( # noqa: C901 if docArgs.length > funcArgs.length: violations.append(v102) + if self.typeHintsInSignature and funcArgs.noTypeHints(): + violations.append(v106) + + if self.typeHintsInSignature and not funcArgs.hasTypeHintInAllArgs(): + violations.append(v107) + + if not self.typeHintsInSignature and funcArgs.hasTypeHintInAnyArg(): + violations.append(v108) + + if self.typeHintsInDocstring and docArgs.noTypeHints(): + violations.append(v109) + + if self.typeHintsInDocstring and not docArgs.hasTypeHintInAllArgs(): + violations.append(v110) + + if not self.typeHintsInDocstring and docArgs.hasTypeHintInAnyArg(): + violations.append(v111) + if not docArgs.equals( funcArgs, - checkTypeHint=self.checkTypeHint, + checkTypeHint=True, orderMatters=self.checkArgOrder, ): if docArgs.equals( funcArgs, - checkTypeHint=self.checkTypeHint, + checkTypeHint=True, orderMatters=False, ): violations.append(v104) @@ -345,7 +371,8 @@ def checkArguments( # noqa: C901 checkTypeHint=False, orderMatters=self.checkArgOrder, ): - violations.append(v105) + if self.typeHintsInSignature and self.typeHintsInDocstring: + violations.append(v105) elif docArgs.equals( funcArgs, checkTypeHint=False, diff --git a/tests/data/google/type_hints/cases.py b/tests/data/google/type_hints/cases.py new file mode 100644 index 0000000..e778665 --- /dev/null +++ b/tests/data/google/type_hints/cases.py @@ -0,0 +1,94 @@ +class MyClass: + def __init__(self): + pass + + def func1(self, arg1, arg2) -> int: + """ + Do something + + Args: + arg1: Arg 1 + arg2: Arg 2 + + Returns: + int: The return value + """ + return 1 + + def func2(self, arg1: int, arg2: float) -> int: + """ + Do something + + Args: + arg1: Arg 1 + arg2: Arg 2 + + Returns: + int: The return value + """ + return 1 + + def func3(self, arg1, arg2) -> int: + """ + Do something + + Args: + arg1 (int): Arg 1 + arg2 (float): Arg 2 + + Returns: + int: The return value + """ + return 1 + + def func4(self, arg1: int, arg2: float) -> int: + """ + Do something + + Args: + arg1 (int): Arg 1 + arg2 (float): Arg 2 + + Returns: + int: The return value + """ + return 1 + + def func5(self, arg1, arg2: float) -> int: + """ + Do something + + Args: + arg1 (int): Arg 1 + arg2: Arg 2 + + Returns: + int: The return value + """ + return 1 + + def func6(self, arg1: bool, arg2: float) -> int: + """ + Do something + + Args: + arg1 (int): Arg 1 + arg2 (float): Arg 2 + + Returns: + int: The return value + """ + return 1 + + def func7(self, arg1, arg2: float) -> int: + """ + Do something + + Args: + arg1: Arg 1 + arg2 (float): Arg 2 + + Returns: + int: The return value + """ + return 1 diff --git a/tests/data/numpy/type_hints/cases.py b/tests/data/numpy/type_hints/cases.py new file mode 100644 index 0000000..0dd3ca8 --- /dev/null +++ b/tests/data/numpy/type_hints/cases.py @@ -0,0 +1,129 @@ +class MyClass: + def __init__(self): + pass + + def func1(self, arg1, arg2) -> int: + """ + Do something + + Parameters + ---------- + arg1 + Arg 1 + arg2 + Arg 2 + + Returns + ------- + int + The return value + """ + return 1 + + def func2(self, arg1: int, arg2: float) -> int: + """ + Do something + + Parameters + ---------- + arg1 + Arg 1 + arg2 + Arg 2 + + Returns + ------- + int + The return value + """ + return 1 + + def func3(self, arg1, arg2) -> int: + """ + Do something + + Parameters + ---------- + arg1 : int + Arg 1 + arg2 : float + Arg 2 + + Returns + ------- + int + The return value + """ + return 1 + + def func4(self, arg1: int, arg2: float) -> int: + """ + Do something + + Parameters + ---------- + arg1 : int + Arg 1 + arg2 : float + Arg 2 + + Returns + ------- + int + The return value + """ + return 1 + + def func5(self, arg1, arg2: float) -> int: + """ + Do something + + Parameters + ---------- + arg1 : int + Arg 1 + arg2 : + Arg 2 + + Returns + ------- + int + The return value + """ + return 1 + + def func6(self, arg1: bool, arg2: float) -> int: + """ + Do something + + Parameters + ---------- + arg1 : int + Arg 1 + arg2 : float + Arg 2 + + Returns + ------- + int + The return value + """ + return 1 + + def func7(self, arg1, arg2: float) -> int: + """ + Do something + + Parameters + ---------- + arg1 : + Arg 1 + arg2 : float + Arg 2 + + Returns + ------- + int + The return value + """ + return 1 diff --git a/tests/test_main.py b/tests/test_main.py index 06cecf1..effa952 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -11,56 +11,19 @@ DATA_DIR = THIS_DIR / 'data' -expectedViolations_True_True = [ - 'DOC101: Method `MyClass.func1_3`: Docstring contains fewer arguments than in ' - 'function signature. ', - 'DOC103: Method `MyClass.func1_3`: Docstring arguments are different from ' - 'function arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in ' - 'the docstring: [arg1: str, arg2: list[int]].', - 'DOC102: Method `MyClass.func1_6`: Docstring contains more arguments than in ' - 'function signature. ', - 'DOC103: Method `MyClass.func1_6`: Docstring arguments are different from ' - 'function arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the docstring but not in the ' - 'function signature: [arg1: int].', - 'DOC101: Method `MyClass.func2`: Docstring contains fewer arguments than in ' - 'function signature. ', - 'DOC103: Method `MyClass.func2`: Docstring arguments are different from ' - 'function arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in ' - 'the docstring: [arg2: float | int | None].', - 'DOC102: Method `MyClass.func3`: Docstring contains more arguments than in ' - 'function signature. ', - 'DOC103: Method `MyClass.func3`: Docstring arguments are different from ' - 'function arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the docstring but not in the ' - '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 ', - '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 ', - 'DOC101: Function `func72`: Docstring contains fewer arguments than in ' - 'function signature. ', - 'DOC103: Function `func72`: Docstring arguments are different from function ' - 'arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in the ' - 'docstring: [arg3: list, arg4: tuple, arg5: dict].', -] - -expectedViolations_False_True = [ +expectedViolations_True = [ 'DOC101: Method `MyClass.func1_3`: Docstring contains fewer arguments than in ' 'function signature. ', + 'DOC109: Method `MyClass.func1_3`: The option `--type-hints-in-docstring` is `True` ' + 'but there are no type hints in the docstring arg list ', 'DOC103: Method `MyClass.func1_3`: Docstring arguments are different from ' 'function arguments. (Or could be other formatting issues: ' 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in ' 'the docstring: [arg1: str, arg2: list[int]].', 'DOC102: Method `MyClass.func1_6`: Docstring contains more arguments than in ' 'function signature. ', + 'DOC106: Method `MyClass.func1_6`: The option `--type-hints-in-signature` is `True` ' + 'but there are no type hints in the signature ', 'DOC103: Method `MyClass.func1_6`: Docstring arguments are different from ' 'function arguments. (Or could be other formatting issues: ' 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the docstring but not in the ' @@ -79,25 +42,33 @@ '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 ', '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 ', 'DOC101: Function `func72`: Docstring contains fewer arguments than in ' 'function signature. ', + 'DOC109: Function `func72`: The option `--type-hints-in-docstring` is `True` ' + 'but there are no type hints in the docstring arg list ', 'DOC103: Function `func72`: Docstring arguments are different from function ' 'arguments. (Or could be other formatting issues: ' 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in the ' 'docstring: [arg3: list, arg4: tuple, arg5: dict].', ] -expectedViolations_True_False = [ +expectedViolations_False = [ 'DOC101: Method `MyClass.func1_3`: Docstring contains fewer arguments than in ' 'function signature. ', + 'DOC109: Method `MyClass.func1_3`: The option `--type-hints-in-docstring` is `True` ' + 'but there are no type hints in the docstring arg list ', 'DOC103: Method `MyClass.func1_3`: Docstring arguments are different from ' 'function arguments. (Or could be other formatting issues: ' 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in ' 'the docstring: [arg1: str, arg2: list[int]].', 'DOC102: Method `MyClass.func1_6`: Docstring contains more arguments than in ' 'function signature. ', + 'DOC106: Method `MyClass.func1_6`: The option `--type-hints-in-signature` is `True` ' + 'but there are no type hints in the signature ', 'DOC103: Method `MyClass.func1_6`: Docstring arguments are different from ' 'function arguments. (Or could be other formatting issues: ' 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the docstring but not in the ' @@ -120,84 +91,36 @@ 'match ', 'DOC101: Function `func72`: Docstring contains fewer arguments than in ' 'function signature. ', + 'DOC109: Function `func72`: The option `--type-hints-in-docstring` is `True` ' + 'but there are no type hints in the docstring arg list ', 'DOC103: Function `func72`: Docstring arguments are different from function ' 'arguments. (Or could be other formatting issues: ' 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in the ' 'docstring: [arg3: list, arg4: tuple, arg5: dict].', ] -expectedViolations_False_False = [ - 'DOC101: Method `MyClass.func1_3`: Docstring contains fewer arguments than in ' - 'function signature. ', - 'DOC103: Method `MyClass.func1_3`: Docstring arguments are different from ' - 'function arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in ' - 'the docstring: [arg1: str, arg2: list[int]].', - 'DOC102: Method `MyClass.func1_6`: Docstring contains more arguments than in ' - 'function signature. ', - 'DOC103: Method `MyClass.func1_6`: Docstring arguments are different from ' - 'function arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the docstring but not in the ' - 'function signature: [arg1: int].', - 'DOC101: Method `MyClass.func2`: Docstring contains fewer arguments than in ' - 'function signature. ', - 'DOC103: Method `MyClass.func2`: Docstring arguments are different from ' - 'function arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in ' - 'the docstring: [arg2: float | int | None].', - 'DOC102: Method `MyClass.func3`: Docstring contains more arguments than in ' - 'function signature. ', - 'DOC103: Method `MyClass.func3`: Docstring arguments are different from ' - 'function arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the docstring but not in the ' - 'function signature: [arg3: Optional[Union[float, int, str]]].', - 'DOC101: Function `func72`: Docstring contains fewer arguments than in ' - 'function signature. ', - 'DOC103: Function `func72`: Docstring arguments are different from function ' - 'arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in the ' - 'docstring: [arg3: list, arg4: tuple, arg5: dict].', -] - -expectedViolationsLookup: Dict[str, List[str]] = { - 'true_true': expectedViolations_True_True, - 'true_false': expectedViolations_True_False, - 'false_true': expectedViolations_False_True, - 'false_false': expectedViolations_False_False, +expectedViolationsLookup: Dict[bool, List[str]] = { + True: expectedViolations_True, + False: expectedViolations_False, } -optionDictLookup: Dict[str, Dict[str, bool]] = { - 'true_true': {'checkTypeHint': True, 'checkArgOrder': True}, - 'true_false': {'checkTypeHint': True, 'checkArgOrder': False}, - 'false_true': {'checkTypeHint': False, 'checkArgOrder': True}, - 'false_false': {'checkTypeHint': False, 'checkArgOrder': False}, -} - -options = [ - 'true_true', - 'true_false', - 'false_true', - 'false_false', -] - @pytest.mark.parametrize( - 'style, filename, option', + 'style, filename, checkArgOrder', list( itertools.product( ['numpy', 'google'], ['function.py', 'classmethod.py', 'method.py', 'staticmethod.py'], - options, + [True, False], ), ), ) def testArguments( style: str, filename: str, - option: str, + checkArgOrder: str, ) -> None: - optionDict: Dict[str, bool] = optionDictLookup[option] - expectedViolations: List[str] = expectedViolationsLookup[option] + expectedViolations: List[str] = expectedViolationsLookup[checkArgOrder] expectedViolationsCopy = copy.deepcopy(expectedViolations) if filename == 'function.py': @@ -205,8 +128,7 @@ def testArguments( violations = _checkFile( filename=DATA_DIR / f'{style}/args/{filename}', - checkTypeHint=optionDict['checkTypeHint'], - checkArgOrder=optionDict['checkArgOrder'], + checkArgOrder=checkArgOrder, style=style, ) assert list(map(str, violations)) == expectedViolationsCopy @@ -262,9 +184,13 @@ def _tweakViolationMsgForFunctions(expectedViolationsCopy: List[str]) -> None: ) -expected_True = [ +expected_skipCheckingShortDocstrings_True = [ 'DOC101: Function `func3`: Docstring contains fewer arguments than in ' 'function signature. ', + 'DOC106: Function `func3`: The option `--type-hints-in-signature` is `True` ' + 'but there are no type hints in the signature ', + 'DOC107: Function `func3`: The option `--type-hints-in-signature` is `True` ' + 'but not all args in the signature have type hints ', 'DOC103: Function `func3`: Docstring arguments are different from function ' 'arguments. (Or could be other formatting issues: ' 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in the ' @@ -273,28 +199,45 @@ def _tweakViolationMsgForFunctions(expectedViolationsCopy: List[str]) -> None: 'DOC201: Function `func3` does not have a return section in docstring ', ] -expected_False = [ +expected_skipCheckingShortDocstrings_False = [ 'DOC101: Function `func1`: Docstring contains fewer arguments than in ' 'function signature. ', + 'DOC106: Function `func1`: The option `--type-hints-in-signature` is `True` ' + 'but there are no type hints in the signature ', + 'DOC107: Function `func1`: The option `--type-hints-in-signature` is `True` ' + 'but not all args in the signature have type hints ', + 'DOC109: Function `func1`: The option `--type-hints-in-docstring` is `True` ' + 'but there are no type hints in the docstring arg list ', 'DOC103: Function `func1`: Docstring arguments are different from function ' 'arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in the ' - 'docstring: [arg1: , arg2: , arg3: ].', + 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the ' + 'function signature but not in the docstring: [arg1: , arg2: , arg3: ].', 'DOC201: Function `func1` does not have a return section in docstring ', 'DOC101: Function `func2`: Docstring contains fewer arguments than in ' 'function signature. ', + 'DOC106: Function `func2`: The option `--type-hints-in-signature` is `True` ' + 'but there are no type hints in the signature ', + 'DOC107: Function `func2`: The option `--type-hints-in-signature` is `True` ' + 'but not all args in the signature have type hints ', + 'DOC109: Function `func2`: The option `--type-hints-in-docstring` is `True` ' + 'but there are no type hints in the docstring arg list ', 'DOC103: Function `func2`: Docstring arguments are different from function ' 'arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in the ' - 'docstring: [arg1: , arg2: , arg3: ].', + 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the ' + 'function signature but not in the docstring: [arg1: , arg2: , arg3: ].', 'DOC201: Function `func2` does not have a return section in docstring ', 'DOC101: Function `func3`: Docstring contains fewer arguments than in ' 'function signature. ', + 'DOC106: Function `func3`: The option `--type-hints-in-signature` is `True` ' + 'but there are no type hints in the signature ', + 'DOC107: Function `func3`: The option `--type-hints-in-signature` is `True` ' + 'but not all args in the signature have type hints ', 'DOC103: Function `func3`: Docstring arguments are different from function ' 'arguments. (Or could be other formatting issues: ' - 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in the ' - 'docstring: [arg1: , arg2: , arg3: ]. Arguments in the docstring but not in ' - 'the function signature: [var1: int, var2: str].', + 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the ' + 'function signature but not in the docstring: [arg1: , arg2: , arg3: ]. ' + 'Arguments in the docstring but not in the function signature: [var1: int, ' + 'var2: str].', 'DOC201: Function `func3` does not have a return section in docstring ', ] @@ -302,10 +245,10 @@ def _tweakViolationMsgForFunctions(expectedViolationsCopy: List[str]) -> None: @pytest.mark.parametrize( 'style, skipCheckingShortDocstrings, expected', [ - ('numpy', True, expected_True), - ('numpy', False, expected_False), - ('google', True, expected_True), - ('google', False, expected_False), + ('numpy', True, expected_skipCheckingShortDocstrings_True), + ('numpy', False, expected_skipCheckingShortDocstrings_False), + ('google', True, expected_skipCheckingShortDocstrings_True), + ('google', False, expected_skipCheckingShortDocstrings_False), ], ) def testSkipCheckingShortDocstrings( @@ -430,6 +373,8 @@ def testRaises(style: str, skipRaisesCheck: bool) -> None: violations = _checkFile( filename=DATA_DIR / f'{style}/raises/cases.py', skipCheckingRaises=skipRaisesCheck, + typeHintsInSignature=False, + typeHintsInDocstring=False, style=style, ) expected0 = [ @@ -452,11 +397,15 @@ def testStarsInArgumentList(style: str) -> None: style=style, ) expected = [ + 'DOC110: Function `func2`: The option `--type-hints-in-docstring` is `True` ' + 'but not all args in the docstring arg list have type hints ', 'DOC103: Function `func2`: Docstring arguments are different from function ' 'arguments. (Or could be other formatting issues: ' 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in the ' 'docstring: [**kwargs: ]. Arguments in the docstring but not in the function ' 'signature: [kwargs: ].', + 'DOC110: Function `func4`: The option `--type-hints-in-docstring` is `True` ' + 'but not all args in the docstring arg list have type hints ', 'DOC103: Function `func4`: Docstring arguments are different from function ' 'arguments. (Or could be other formatting issues: ' 'https://github.com/jsh9/pydoclint#notes-on-doc103 ). Arguments in the function signature but not in the ' @@ -494,6 +443,8 @@ def testParsingErrors_google() -> None: def testParsingErrors_numpy() -> None: violations = _checkFile( filename=DATA_DIR / 'numpy/parsing_errors/cases.py', + typeHintsInDocstring=False, + typeHintsInSignature=False, style='numpy', ) expected = [ @@ -553,6 +504,137 @@ def testPropertyMethod(style: str) -> None: assert list(map(str, violations)) == expected +@pytest.mark.parametrize( + 'style, typeHintsInDocstring, typeHintsInSignature', + itertools.product( + ['numpy', 'google'], + [False, True], + [False, True], + ), +) +def testTypeHintChecking( + style: str, + typeHintsInDocstring: bool, + typeHintsInSignature: bool, +) -> None: + violations = _checkFile( + filename=DATA_DIR / f'{style}/type_hints/cases.py', + style=style, + typeHintsInDocstring=typeHintsInDocstring, + typeHintsInSignature=typeHintsInSignature, + ) + + expected_lookup = { + (False, False): [ + 'DOC108: Method `MyClass.func2`: The option `--type-hints-in-signature` is ' + '`False` but there are type hints in the signature ', + 'DOC111: Method `MyClass.func3`: The option `--type-hints-in-docstring` is ' + '`False` but there are type hints in the docstring arg list ', + 'DOC108: Method `MyClass.func4`: The option `--type-hints-in-signature` is ' + '`False` but there are type hints in the signature ', + 'DOC111: Method `MyClass.func4`: The option `--type-hints-in-docstring` is ' + '`False` but there are type hints in the docstring arg list ', + 'DOC108: Method `MyClass.func5`: The option `--type-hints-in-signature` is ' + '`False` but there are type hints in the signature ', + 'DOC111: Method `MyClass.func5`: The option `--type-hints-in-docstring` is ' + '`False` but there are type hints in the docstring arg list ', + 'DOC108: Method `MyClass.func6`: The option `--type-hints-in-signature` is ' + '`False` but there are type hints in the signature ', + 'DOC111: Method `MyClass.func6`: The option `--type-hints-in-docstring` is ' + '`False` but there are type hints in the docstring arg list ', + 'DOC108: Method `MyClass.func7`: The option `--type-hints-in-signature` is ' + '`False` but there are type hints in the signature ', + 'DOC111: Method `MyClass.func7`: The option `--type-hints-in-docstring` is ' + '`False` but there are type hints in the docstring arg list ', + ], + (False, True): [ + 'DOC106: Method `MyClass.func1`: The option `--type-hints-in-signature` is ' + '`True` but there are no type hints in the signature ', + 'DOC107: Method `MyClass.func1`: The option `--type-hints-in-signature` is ' + '`True` but not all args in the signature have type hints ', + 'DOC106: Method `MyClass.func3`: The option `--type-hints-in-signature` is ' + '`True` but there are no type hints in the signature ', + 'DOC107: Method `MyClass.func3`: The option `--type-hints-in-signature` is ' + '`True` but not all args in the signature have type hints ', + 'DOC111: Method `MyClass.func3`: The option `--type-hints-in-docstring` is ' + '`False` but there are type hints in the docstring arg list ', + 'DOC111: Method `MyClass.func4`: The option `--type-hints-in-docstring` is ' + '`False` but there are type hints in the docstring arg list ', + 'DOC107: Method `MyClass.func5`: The option `--type-hints-in-signature` is ' + '`True` but not all args in the signature have type hints ', + 'DOC111: Method `MyClass.func5`: The option `--type-hints-in-docstring` is ' + '`False` but there are type hints in the docstring arg list ', + 'DOC111: Method `MyClass.func6`: The option `--type-hints-in-docstring` is ' + '`False` but there are type hints in the docstring arg list ', + 'DOC107: Method `MyClass.func7`: The option `--type-hints-in-signature` is ' + '`True` but not all args in the signature have type hints ', + 'DOC111: Method `MyClass.func7`: The option `--type-hints-in-docstring` is ' + '`False` but there are type hints in the docstring arg list ', + ], + (True, False): [ + 'DOC109: Method `MyClass.func1`: The option `--type-hints-in-docstring` is ' + '`True` but there are no type hints in the docstring arg list ', + 'DOC110: Method `MyClass.func1`: The option `--type-hints-in-docstring` is ' + '`True` but not all args in the docstring arg list have type hints ', + 'DOC108: Method `MyClass.func2`: The option `--type-hints-in-signature` is ' + '`False` but there are type hints in the signature ', + 'DOC109: Method `MyClass.func2`: The option `--type-hints-in-docstring` is ' + '`True` but there are no type hints in the docstring arg list ', + 'DOC110: Method `MyClass.func2`: The option `--type-hints-in-docstring` is ' + '`True` but not all args in the docstring arg list have type hints ', + 'DOC108: Method `MyClass.func4`: The option `--type-hints-in-signature` is ' + '`False` but there are type hints in the signature ', + 'DOC108: Method `MyClass.func5`: The option `--type-hints-in-signature` is ' + '`False` but there are type hints in the signature ', + 'DOC110: Method `MyClass.func5`: The option `--type-hints-in-docstring` is ' + '`True` but not all args in the docstring arg list have type hints ', + 'DOC108: Method `MyClass.func6`: The option `--type-hints-in-signature` is ' + '`False` but there are type hints in the signature ', + 'DOC108: Method `MyClass.func7`: The option `--type-hints-in-signature` is ' + '`False` but there are type hints in the signature ', + 'DOC110: Method `MyClass.func7`: The option `--type-hints-in-docstring` is ' + '`True` but not all args in the docstring arg list have type hints ', + ], + (True, True): [ + 'DOC106: Method `MyClass.func1`: The option `--type-hints-in-signature` is ' + '`True` but there are no type hints in the signature ', + 'DOC107: Method `MyClass.func1`: The option `--type-hints-in-signature` is ' + '`True` but not all args in the signature have type hints ', + 'DOC109: Method `MyClass.func1`: The option `--type-hints-in-docstring` is ' + '`True` but there are no type hints in the docstring arg list ', + 'DOC110: Method `MyClass.func1`: The option `--type-hints-in-docstring` is ' + '`True` but not all args in the docstring arg list have type hints ', + 'DOC109: Method `MyClass.func2`: The option `--type-hints-in-docstring` is ' + '`True` but there are no type hints in the docstring arg list ', + 'DOC110: Method `MyClass.func2`: The option `--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 ', + 'DOC106: Method `MyClass.func3`: The option `--type-hints-in-signature` is ' + '`True` but there are no type hints in the signature ', + 'DOC107: Method `MyClass.func3`: The option `--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 ', + 'DOC107: Method `MyClass.func5`: The option `--type-hints-in-signature` is ' + '`True` but not all args in the signature have type hints ', + 'DOC110: Method `MyClass.func5`: The option `--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 ', + 'DOC107: Method `MyClass.func7`: The option `--type-hints-in-signature` is ' + '`True` but not all args in the signature have type hints ', + 'DOC110: Method `MyClass.func7`: The option `--type-hints-in-docstring` is ' + '`True` but not all args in the docstring arg list have type hints ', + ], + } + + expected = expected_lookup[(typeHintsInDocstring, typeHintsInSignature)] + assert list(map(str, violations)) == expected + + def testPlayground() -> None: """ This is a placeholder test for testing the `playground.py` file.