Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vague error message when the docstring of a member is dynamically assigned. #177

Open
austinyu opened this issue Oct 22, 2024 · 0 comments

Comments

@austinyu
Copy link

Consider this simple example



class A:
    def __init__(self):
        pass

    def method_a(self):
        """This is method_a"""

class B:
    def __init__(self):
        pass

    def method_b(self):
        pass

    method_b.__doc__ = A.method_a.__doc__

When I run pydoclint, I get this long traceback

  File "...\.venv\Lib\site-packages\click\core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\click\core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\click\core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\click\core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\click\decorators.py", line 33, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\pydoclint\main.py", line 393, in main
    violationsInAllFiles: Dict[str, List[Violation]] = _checkPaths(
                                                       ^^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\pydoclint\main.py", line 564, in _checkPaths
    violationsInThisFile: List[Violation] = _checkFile(
                                            ^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\pydoclint\main.py", line 645, in _checkFile
    visitor.visit(tree)
  File "...\AppData\Local\Programs\Python\Python312\Lib\ast.py", 
line 407, in visit
    return visitor(node)
           ^^^^^^^^^^^^^
  File "...\AppData\Local\Programs\Python\Python312\Lib\ast.py", 
line 415, in generic_visit
    self.visit(item)
  File "...\AppData\Local\Programs\Python\Python312\Lib\ast.py", 
line 407, in visit
    return visitor(node)
           ^^^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\pydoclint\visitor.py", line 101, in visit_ClassDef
    checkClassAttributesAgainstClassDocstring(
  File "...\.venv\Lib\site-packages\pydoclint\utils\visitor_helper.py", line 44, in checkClassAttributesAgainstClassDocstring
    actualArgs: ArgList = extractClassAttributesFromNode(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\pydoclint\utils\visitor_helper.py", line 167, in extractClassAttributesFromNode
    atl.extend(ArgList.fromAstAssign(itm).infoList)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\.venv\Lib\site-packages\pydoclint\utils\arg.py", line 224, in fromAstAssign
    raise InternalError(
pydoclint.utils.internal_error.InternalError: astAssign.targets[0] is of type <class 'ast.Attribute'>

I have a workaround here.

class A:
    def __init__(self):
        pass

    def method_a(self):
        """This is method_a"""

class B:
    def __init__(self):
        pass

    def method_b(self):
        pass

B.method_b.__doc__ = A.method_a.__doc__

However, I think fixing this error should not be that challenging. As far as I understand, we need another case for Attribute here
pydoclint\utils\arg.py", line 224

    @classmethod
    def fromAstAssign(cls, astAssign: ast.Assign) -> 'ArgList':
        """Construct an ArgList from variable declaration/assignment"""
        infoList: List[Arg] = []
        for i, target in enumerate(astAssign.targets):
            if isinstance(target, ast.Tuple):  # such as `a, b = c, d = 1, 2`
                for j, item in enumerate(target.elts):
                    if not isinstance(item, ast.Name):
                        raise InternalError(
                            f'astAssign.targets[{i}].elts[{j}] is of'
                            f' type {type(item)} instead of ast.Name'
                        )

                    infoList.append(Arg(name=item.id, typeHint=''))
            elif isinstance(target, ast.Name):  # such as `a = 1` or `a = b = 2`
                infoList.append(Arg(name=target.id, typeHint=''))
            else:
                raise InternalError(
                    f'astAssign.targets[{i}] is of type {type(target)}'
                )

        return ArgList(infoList=infoList)
     ```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant