diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ae35cd..ab8f4a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Fixed - Fixed a bug in unparsing annotations when checking class attributes + - Fixed a bug in checking class attributes where there are no attributes in + class def or in docstring ## [0.5.0] - 2024-06-22 diff --git a/pydoclint/utils/visitor_helper.py b/pydoclint/utils/visitor_helper.py index 4f9e0ef..3e264e2 100644 --- a/pydoclint/utils/visitor_helper.py +++ b/pydoclint/utils/visitor_helper.py @@ -32,14 +32,22 @@ def checkClassAttributesAgainstClassDocstring( ) -> None: """Check class attribute list against the attribute list in docstring""" classAttributes = _collectClassAttributes(node) - - if len(classAttributes) == 0: - return - actualArgs: ArgList = _convertClassAttributesIntoArgList(classAttributes) classDocstring: str = getDocstring(node) - doc: Doc = Doc(docstring=classDocstring, style=style) + try: + doc: Doc = Doc(docstring=classDocstring, style=style) + except Exception as excp: + doc = Doc(docstring='', style=style) + violations.append( + Violation( + code=1, + line=node.lineno, + msgPrefix=f'Class `{node.name}`:', + msgPostfix=str(excp).replace('\n', ' '), + ) + ) + docArgs: ArgList = doc.attrList checkDocArgsLengthAgainstActualArgs( diff --git a/tests/data/google/class_attributes/cases.py b/tests/data/google/class_attributes/cases.py index c359e65..976be2c 100644 --- a/tests/data/google/class_attributes/cases.py +++ b/tests/data/google/class_attributes/cases.py @@ -100,3 +100,24 @@ def do_something(self, arg2: bool) -> int: int: Result """ return 2 + + +class MyClass4: + """ + This is a class + + Attributes: + name (str): My name + """ + + def __int__(self): + pass + + +@dataclass +class MyClass5: + """ + This is a class + """ + + morning: str diff --git a/tests/data/numpy/class_attributes/cases.py b/tests/data/numpy/class_attributes/cases.py index aa0d3f2..509daec 100644 --- a/tests/data/numpy/class_attributes/cases.py +++ b/tests/data/numpy/class_attributes/cases.py @@ -125,3 +125,26 @@ def do_something(self, arg2: bool) -> int: Result """ return 2 + + +class MyClass4: + """ + This is a class + + Attributes + ---------- + name : str + My name + """ + + def __int__(self): + pass + + +@dataclass +class MyClass5: + """ + This is a class + """ + + morning: str diff --git a/tests/data/sphinx/allow_init_docstring/cases.py b/tests/data/sphinx/allow_init_docstring/cases.py index fb51709..a7ec073 100644 --- a/tests/data/sphinx/allow_init_docstring/cases.py +++ b/tests/data/sphinx/allow_init_docstring/cases.py @@ -95,7 +95,7 @@ class E: A class that does something :attr attr1: - :attr attr1: Arg 2 + :attr attr2: Arg 2 """ def __init__(self, arg1: int, arg2: float) -> None: diff --git a/tests/data/sphinx/class_attributes/cases.py b/tests/data/sphinx/class_attributes/cases.py index 5af1972..86a6c5f 100644 --- a/tests/data/sphinx/class_attributes/cases.py +++ b/tests/data/sphinx/class_attributes/cases.py @@ -101,3 +101,24 @@ def do_something(self, arg2: bool) -> int: :rtype: int """ return 2 + + +class MyClass4: + """ + This is a class + + :attr name: My name + :type name: str + """ + + def __int__(self): + pass + + +@dataclass +class MyClass5: + """ + This is a class + """ + + morning: str diff --git a/tests/test_main.py b/tests/test_main.py index 38d7c0d..de32297 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -201,6 +201,19 @@ def testClassAttributes( 'name: str].', 'DOC105: Method `MyClass3.do_something`: Argument names match, but type hints ' 'in these args do not match: arg2', + 'DOC602: Class `MyClass4`: Class docstring contains more class attributes ' + 'than in actual class attributes. ', + 'DOC603: Class `MyClass4`: Class docstring attributes are different from ' + 'actual class attributes. (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 actual class attributes: [name: ' + 'str].', + 'DOC601: Class `MyClass5`: Class docstring contains fewer class attributes ' + 'than actual class attributes. ', + 'DOC603: Class `MyClass5`: Class docstring attributes are different from ' + 'actual class attributes. (Or could be other formatting issues: ' + 'https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). ' + 'Attributes in the class definition but not in the docstring: [morning: str].', ], False: [ 'DOC105: Method `MyClass1.__init__`: Argument names match, but type hints in ' @@ -584,6 +597,13 @@ def testAllowInitDocstring(style: str) -> None: 'there are no "yield" statements, or the return annotation is not a ' 'Generator/Iterator/Iterable. (Or it could be because the function lacks a ' 'return annotation.) ', + 'DOC602: Class `E`: Class docstring contains more class attributes than in ' + 'actual class attributes. ', + 'DOC603: Class `E`: Class docstring attributes are different from actual ' + 'class attributes. (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 actual class attributes: [attr1: , ' + 'attr2: ].', ] assert list(map(str, violations)) == expected @@ -858,8 +878,10 @@ def testParsingErrors_google() -> None: style='google', ) expected = [ + 'DOC001: Class `A`: Potential formatting errors in docstring. Error message: ' + "Expected a colon in 'arg1'.", 'DOC001: Function/method `__init__`: Potential formatting errors in ' - "docstring. Error message: Expected a colon in 'arg1'." + "docstring. Error message: Expected a colon in 'arg1'.", ] assert list(map(str, violations)) == expected