Skip to content

Commit

Permalink
Better handling of backslash in docstring (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsh9 committed Aug 22, 2023
1 parent 3ba5d65 commit 5e24b6e
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 11 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Change Log

## [0.2.2] - 2023-08-22

- Improved

- Improved handling of escape symbol (`\`) in docstrings
(https://github.com/jsh9/pydoclint/issues/73)

- Full diff
- https://github.com/jsh9/pydoclint/compare/0.2.1...0.2.2

## [0.2.1] - 2023-08-21

- Improved
Expand Down
36 changes: 27 additions & 9 deletions pydoclint/utils/arg.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ def __repr__(self) -> str:
def __str__(self) -> str:
return f'{self.name}: {self.typeHint}'

def __eq__(self, o: 'Arg') -> bool:
if not isinstance(o, Arg):
def __eq__(self, other: 'Arg') -> bool:
if not isinstance(other, Arg):
return False

return self.name == o.name and self._eq(self.typeHint, o.typeHint)
argNamesEqual: bool = self._argNamesEq(self.name, other.name)
typeHintsEqual: bool = self._typeHintsEq(self.typeHint, other.typeHint)
return argNamesEqual and typeHintsEqual

def __lt__(self, other: 'Arg') -> bool:
if not isinstance(other, Arg):
Expand Down Expand Up @@ -91,7 +93,7 @@ def _str(cls, typeName: Optional[str]) -> str:
return '' if typeName is None else typeName

@classmethod
def _eq(cls, str1: str, str2: str) -> bool:
def _typeHintsEq(cls, hint1: str, hint2: str) -> bool:
# We parse and then unparse so that cases like this can be
# treated as equal:
#
Expand All @@ -103,16 +105,32 @@ def _eq(cls, str1: str, str2: str) -> bool:
# >>> "ghi",
# >>> ]
try:
str1_: str = unparseAnnotation(ast.parse(stripQuotes(str1)))
hint1_: str = unparseAnnotation(ast.parse(stripQuotes(hint1)))
except SyntaxError:
str1_ = str1
hint1_ = hint1

try:
str2_: str = unparseAnnotation(ast.parse(stripQuotes(str2)))
hint2_: str = unparseAnnotation(ast.parse(stripQuotes(hint2)))
except SyntaxError:
str2_ = str2
hint2_ = hint2

return hint1_ == hint2_

@classmethod
def _argNamesEq(cls, name1: str, name2: str) -> bool:
return cls._removeEscapeChar(name1) == cls._removeEscapeChar(name2)

return str1_ == str2_
@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).
#
# For example:
# arg1\_\_ (int): The first argument
return string.replace('\\', '')


class ArgList:
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = pydoclint
version = 0.2.1
version = 0.2.2
description = A Python docstring linter that checks arguments, returns, yields, and raises sections
long_description = file: README.md
long_description_content_type = text/markdown
Expand Down
29 changes: 29 additions & 0 deletions tests/data/edge_cases/05_escape_char/google.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
def myFunc(
arg1_: int,
arg2__: int,
arg3___: int,
arg4____: int,
some_thing_1_: int,
some_thing_2__: int,
some_thing_3___: int,
some_thing_4____: int,
some_thing_5_____: str,
) -> None:
r"""
Do something.
Args:
arg1\_ (int): Arg
arg2\_\_ (int): Arg
arg3\_\_\_ (int): Arg
arg4\_\_\_\_ (int): Arg
some_thing_1\_ (int): Arg
some_thing_2\_\_ (int): Arg
some_thing_3\_\_\_ (int): Arg
some_thing_4\_\_\_\_ (int): Arg
some_thing_5_____ (str): Arg
Returns:
None: Return value
"""
pass
41 changes: 41 additions & 0 deletions tests/data/edge_cases/05_escape_char/numpy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
def myFunc(
arg1_: int,
arg2__: int,
arg3___: int,
arg4____: int,
some_thing_1_: int,
some_thing_2__: int,
some_thing_3___: int,
some_thing_4____: int,
some_thing_5_____: str,
) -> None:
r"""
Do something.
Parameters
----------
arg1\_ : int
Arg
arg2\_\_ : int
Arg
arg3\_\_\_ : int
Arg
arg4\_\_\_\_ : int
Arg
some_thing_1\_ : int
Arg
some_thing_2\_\_ : int
Arg
some_thing_3\_\_\_ : int
Arg
some_thing_4\_\_\_\_ : int
Arg
some_thing_5_____ : str
Arg
Returns
-------
None
Return value
"""
pass
36 changes: 36 additions & 0 deletions tests/data/edge_cases/05_escape_char/sphinx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
def myFunc(
arg1_: int,
arg2__: int,
arg3___: int,
arg4____: int,
some_thing_1_: int,
some_thing_2__: int,
some_thing_3___: int,
some_thing_4____: int,
some_thing_5_____: str,
) -> None:
r"""
Do something.
:param arg1\_: Arg
:type arg1\_: int
:param arg2\_\_: Arg
:type arg2\_\_: int
:param arg3\_\_\_: Arg
:type arg3\_\_\_: int
:param arg4\_\_\_\_: Arg
:type arg4\_\_\_\_: int
:param some_thing_1\_: Arg
:type some_thing_1\_: int
:param some_thing_2\_\_: Arg
:type some_thing_2\_\_: int
:param some_thing_3\_\_\_: Arg
:type some_thing_3\_\_\_: int
:param some_thing_4\_\_\_\_: Arg
:type some_thing_4\_\_\_\_: int
:param some_thing_5_____: Arg
:type some_thing_5_____: str
:return: Return value
:rtype: None
"""
pass
3 changes: 3 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,9 @@ def testNonAscii() -> None:
{'style': 'numpy'},
[],
),
('05_escape_char/google.py', {'style': 'google'}, []),
('05_escape_char/numpy.py', {'style': 'numpy'}, []),
('05_escape_char/sphinx.py', {'style': 'sphinx'}, []),
],
)
def testEdgeCases(
Expand Down
2 changes: 1 addition & 1 deletion tests/utils/test_arg.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def testArg_sorting(original: Set[Arg], after: List[Arg]) -> None:
],
)
def testArg_eq(str1: str, str2: str, expected: bool) -> None:
assert Arg._eq(str1, str2) == expected
assert Arg._typeHintsEq(str1, str2) == expected


@pytest.mark.parametrize(
Expand Down

0 comments on commit 5e24b6e

Please sign in to comment.