diff --git a/CHANGELOG.md b/CHANGELOG.md index d9bf83f..22f9daa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## [0.3.8] - 2023-10-20 + +- Fixed + + - A bug in handling prepended escape characters in docstrings + +- Full diff + - https://github.com/jsh9/pydoclint/compare/0.3.7...0.3.8 + ## [0.3.7] - 2023-10-19 - Changed diff --git a/pydoclint/utils/arg.py b/pydoclint/utils/arg.py index 4627c8b..7ce6b40 100644 --- a/pydoclint/utils/arg.py +++ b/pydoclint/utils/arg.py @@ -19,7 +19,7 @@ def __init__(self, name: str, typeHint: str) -> None: if len(name) == 0: raise ValueError('`name` cannot be an empty string') - self.name: str = name + self.name: str = self._removeEscapeChar(name) self.typeHint: str = typeHint def __repr__(self) -> str: @@ -32,7 +32,7 @@ def __eq__(self, other: 'Arg') -> bool: if not isinstance(other, Arg): return False - argNamesEqual: bool = self._argNamesEq(self.name, other.name) + argNamesEqual: bool = self.name == other.name typeHintsEqual: bool = self._typeHintsEq(self.typeHint, other.typeHint) return argNamesEqual and typeHintsEqual @@ -110,20 +110,14 @@ def _typeHintsEq(cls, hint1: str, hint2: str) -> bool: return hint1_ == hint2_ - @classmethod - def _argNamesEq(cls, name1: str, name2: str) -> bool: - return cls._removeEscapeChar(name1) == cls._removeEscapeChar(name2) - @classmethod def _removeEscapeChar(cls, string: str) -> str: - # We need to remove `\` from the arg names before comparing them, - # because when there are 1 or 2 trailing underscores in an argument, - # people need to use `\_` or `\_\_`, otherwise Sphinx will somehow - # not render the underscores (and for some reason, 3 or more trailing - # underscores are fine). - # + # We need to remove `\` from the arg names for proper comparison. + # This is because it is often necessary to add `\` in docstrings in + # order for Sphinx to correctly render them. # For example: - # arg1\_\_ (int): The first argument + # arg1\_\_ + # \\**kwargs return string.replace('\\', '') diff --git a/setup.cfg b/setup.cfg index e4e32f1..6e4dcd2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pydoclint -version = 0.3.7 +version = 0.3.8 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/data/edge_cases/05_escape_char/google.py b/tests/data/edge_cases/05_escape_char/google.py index 7b545ef..d60e7e5 100644 --- a/tests/data/edge_cases/05_escape_char/google.py +++ b/tests/data/edge_cases/05_escape_char/google.py @@ -1,3 +1,8 @@ +# From these issues: +# https://github.com/jsh9/pydoclint/issues/73 +# https://github.com/jsh9/pydoclint/issues/92 + + def myFunc( arg1_: int, arg2__: int, @@ -8,6 +13,8 @@ def myFunc( some_thing_3___: int, some_thing_4____: int, some_thing_5_____: str, + *args: Any, + **kwargs: Any, ) -> None: r""" Do something. @@ -22,6 +29,8 @@ def myFunc( some_thing_3\_\_\_ (int): Arg some_thing_4\_\_\_\_ (int): Arg some_thing_5_____ (str): Arg + *args (Any): Args + **kwargs (Any): Keyword args Returns: None: Return value diff --git a/tests/data/edge_cases/05_escape_char/numpy.py b/tests/data/edge_cases/05_escape_char/numpy.py index 22ecbe1..02fa8c1 100644 --- a/tests/data/edge_cases/05_escape_char/numpy.py +++ b/tests/data/edge_cases/05_escape_char/numpy.py @@ -1,3 +1,8 @@ +# From these issues: +# https://github.com/jsh9/pydoclint/issues/73 +# https://github.com/jsh9/pydoclint/issues/92 + + def myFunc( arg1_: int, arg2__: int, @@ -8,6 +13,8 @@ def myFunc( some_thing_3___: int, some_thing_4____: int, some_thing_5_____: str, + *args: Any, + **kwargs: Any, ) -> None: r""" Do something. @@ -32,6 +39,10 @@ def myFunc( Arg some_thing_5_____ : str Arg + *args : Any + Args + **kwargs : Any + Keyword args Returns ------- diff --git a/tests/data/edge_cases/05_escape_char/sphinx.py b/tests/data/edge_cases/05_escape_char/sphinx.py index ab8fc97..4383a57 100644 --- a/tests/data/edge_cases/05_escape_char/sphinx.py +++ b/tests/data/edge_cases/05_escape_char/sphinx.py @@ -1,3 +1,8 @@ +# From these issues: +# https://github.com/jsh9/pydoclint/issues/73 +# https://github.com/jsh9/pydoclint/issues/92 + + def myFunc( arg1_: int, arg2__: int, @@ -8,6 +13,8 @@ def myFunc( some_thing_3___: int, some_thing_4____: int, some_thing_5_____: str, + *args: Any, + **kwargs: Any, ) -> None: r""" Do something. @@ -30,6 +37,10 @@ def myFunc( :type some_thing_4\_\_\_\_: int :param some_thing_5_____: Arg :type some_thing_5_____: str + :param \\*args: Args + :type \\*args: Any + :param \\**kwargs: Args + :type \\**kwargs: Any :return: Return value :rtype: None """ diff --git a/tests/utils/test_arg.py b/tests/utils/test_arg.py index 8cc07d5..6249569 100644 --- a/tests/utils/test_arg.py +++ b/tests/utils/test_arg.py @@ -16,6 +16,9 @@ def testArg_initializationCheck(): (Arg(name='1', typeHint='2'), '1: 2'), (Arg(name='arg1', typeHint='str'), 'arg1: str'), (Arg(name='obj', typeHint='int | float'), 'obj: int | float'), + (Arg(name='arg1\_\_', typeHint='Any'), 'arg1__: Any'), # noqa: W605 + (Arg(name='**kwargs', typeHint='Any'), '**kwargs: Any'), + (Arg(name='\\**kwargs', typeHint='Any'), '**kwargs: Any'), ], ) def testArg_str(arg: Arg, string_repr: str) -> None: @@ -28,6 +31,10 @@ def testArg_str(arg: Arg, string_repr: str) -> None: (Arg(name='1', typeHint='2'), Arg(name='1', typeHint='2')), (Arg(name='abc', typeHint='12345'), Arg(name='abc', typeHint='12345')), (Arg(name='aa', typeHint=''), Arg(name='aa', typeHint='')), + (Arg(name='\\**kw', typeHint=''), Arg(name='**kw', typeHint='')), + (Arg(name='**kw', typeHint=''), Arg(name='\\**kw', typeHint='')), + (Arg(name='\\*args', typeHint=''), Arg(name='*args', typeHint='')), + (Arg(name='*args', typeHint=''), Arg(name='\\*args', typeHint='')), ], ) def testArg_equal(arg1: Arg, arg2: Arg) -> None: @@ -137,7 +144,7 @@ def testArg_sorting(original: Set[Arg], after: List[Arg]) -> None: ('Literal["abc", "def"]', "Literal[\n 'abc',\n 'def',\n]", True), ], ) -def testArg_eq(str1: str, str2: str, expected: bool) -> None: +def testArg_typeHintsEq(str1: str, str2: str, expected: bool) -> None: assert Arg._typeHintsEq(str1, str2) == expected @@ -193,6 +200,14 @@ def testArgList_length(input_: ArgList, expected: int) -> None: ArgList([Arg('1', '2'), Arg('2', '3'), Arg('3', '4')]), ArgList([Arg('1', '2'), Arg('2', '3'), Arg('3', '4')]), ), + ( + ArgList([Arg('*args', '1'), Arg('\\**kwargs', '2')]), + ArgList([Arg('\\*args', '1'), Arg('**kwargs', '2')]), + ), + ( + ArgList([Arg('arg1\_', '1'), Arg('arg2__', '2')]), # noqa: W605 + ArgList([Arg('arg1_', '1'), Arg('arg2\_\_', '2')]), # noqa: W605 + ), ], ) def testArgList_equality(list1: ArgList, list2: ArgList) -> None: @@ -298,6 +313,16 @@ def testArgList_contains( ArgList([]), {Arg('a', '1'), Arg('b', '2'), Arg('c', '3')}, ), + ( + ArgList([Arg('*args', '1'), Arg('\\**kwargs', '2')]), + ArgList([Arg('\\*args', '1')]), + {Arg('**kwargs', '2')}, + ), + ( + ArgList([Arg('arg1\_', '1'), Arg('arg2__', '2')]), # noqa: W605 + ArgList([Arg('arg2\_\_', '2')]), # noqa: W605 + {Arg('arg1_', '1')}, + ), ], ) def testArgList_subtract(