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

pydoclint generates DOC602 and DOC603 on google style guide example for classes #163

Open
jabbera opened this issue Aug 30, 2024 · 11 comments

Comments

@jabbera
Copy link

jabbera commented Aug 30, 2024

The google style guide for instance class attributes is here: https://google.github.io/styleguide/pyguide.html#384-classes

The example they provide is:

class SampleClass:
    """Summary of class here.

    Longer class information...
    Longer class information...

    Attributes:
        likes_spam: A boolean indicating if we like SPAM or not.
        eggs: An integer count of the eggs we have laid.
    """

    def __init__(self, likes_spam: bool = False):
        """Initializes the instance based on spam preference.

        Args:
          likes_spam: Defines if instance exhibits this preference.
        """
        self.likes_spam = likes_spam
        self.eggs = 0

    @property
    def butter_sticks(self) -> int:
        """The number of butter sticks we have."""

Running this though 0.5.6 pydoclint with the following settings:

[tool.pydoclint]
style = 'google'
allow-init-docstring = true

generates the following errors:

    1: DOC602: Class `SampleClass`: Class docstring contains more class attributes than in actual class attributes.  (Please read https://jsh9.github.io/pydoclint/checking_class_attributes.html on how to correctly document class attributes.)
    1: DOC603: Class `SampleClass`: 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: [eggs: , likes_spam: ]. (Please read https://jsh9.github.io/pydoclint/checking_class_attributes.html on how to correctly document class attributes.)

Can you please tell me how I should be documenting class instance variables?

@jsh9
Copy link
Owner

jsh9 commented Sep 28, 2024

Hi @jabbera ,

Sorry for the delayed reply.

In pydoclint, "class attributes" specifically refer to the attributes above the __init__() function.

In your example, both eggs and likes_spam are instance attributes rather than class attributes. Because both of them can only be initiated when you actually instantiate an instance of this class.

@jabbera
Copy link
Author

jabbera commented Sep 28, 2024

@jsh9 no worries on the delay getting back to me. I think you misunderstood my issue. Google doc style says instance attributes should be defined as in the example above. When I do they Pydoclint complains about class attributes (static fields). My question is how to I document the instance fields?

@jsh9
Copy link
Owner

jsh9 commented Sep 29, 2024

This would work, I think.

class SampleClass:
    """Summary of class here.

    Longer class information...
    Longer class information...
    """

    def __init__(self, likes_spam: bool = False):
        """Initializes the instance based on spam preference.

        Args:
          likes_spam: Defines if instance exhibits this preference.
        """
        self.likes_spam = likes_spam
        self.eggs = 0

    @property
    def butter_sticks(self) -> int:
        """The number of butter sticks we have."""

The trick is to use "Attributes" section in the docstring for class attributes, and use the "Arg" section in the docstring for instance attributes or arguments to the init function.

@jabbera
Copy link
Author

jabbera commented Nov 6, 2024

@jsh9 I appreciate your take on it, but pydoclint isn't following the example google sets forth and since I can't exclude the rule from pydoclint it's really causing a headache.

@Lendemor
Copy link

This would work, I think.

class SampleClass:
    """Summary of class here.

    Longer class information...
    Longer class information...
    """

    def __init__(self, likes_spam: bool = False):
        """Initializes the instance based on spam preference.

        Args:
          likes_spam: Defines if instance exhibits this preference.
        """
        self.likes_spam = likes_spam
        self.eggs = 0

    @property
    def butter_sticks(self) -> int:
        """The number of butter sticks we have."""

The trick is to use "Attributes" section in the docstring for class attributes, and use the "Arg" section in the docstring for instance attributes or arguments to the init function.

This suggestion will throw a DOC301 so it's not a good solution.

@jsh9
Copy link
Owner

jsh9 commented Dec 15, 2024

Hi @Lendemor ,

It is by design that my example above would trigger DOC301. To silence DOC301, you can use the following combination of configs:

  • Set --allow-init-docsting to True
  • Set --arg-type-hints-in-docstring to False

More details about pydoclint's config options are here: https://jsh9.github.io/pydoclint/config_options.html

@jsh9
Copy link
Owner

jsh9 commented Dec 15, 2024

@jsh9 I appreciate your take on it, but pydoclint isn't following the example google sets forth and since I can't exclude the rule from pydoclint it's really causing a headache.

Hi @jabbera, here are more of my thoughts on this particular topic:

It is pydoclint's philosophy that one argument (or attribute) should only be documented in one place, rather than in two places. If an attribute is documented in two places with different type annotation and/or different explanations, it would introduce ambiguity and thus be impossible for a rule-based linter (such as pydoclint) to check. (It would even be almost impossible for a human to do so).

Here's an example:

class MyClass:
    """
    My class

    Attributes:
        data (int): my data
    """
    def __init__(self, data: str) -> None:
        """
        Initialize my class

        Args:
            data (float): your data
        """
        self.data = data

That's why in pydoclint's design, the "Attributes" section is for class attributes, and the "Arg" section is for instance attributes. This creates a natural and unambiguous separation for pydoclint to run checks.

@gaspardc-met
Copy link

Hey,
This doesn't seem to work at the moment (latest pydoclint==0.5.13).
Running with --allow-init-docstring=True, with Parameters in the class docstring and Args in the init docstring, I get:

Class docstring has an argument/parameter section; please put it in the __init__() docstring

And if I do put it in the init docstring it also fails.

@jsh9
Copy link
Owner

jsh9 commented Dec 24, 2024

Hi @gaspardc-met , which code example were you referring to?

@erklem
Copy link

erklem commented Jan 9, 2025

@jsh9 Thanks for putting this tool together. I like the philosophy of not documenting things in two places (DRY principle).

However, not providing flexibility for instance attributes has its own challenges. I'm using dependency injection, so not all my instance attributes are passed as _init_() arguments (simple example below).

Is there any supported way for me to document the instance attributes?

class Combiner():
    """
    Aggregates information
    
    Attributes
    -----------
    data: list
        Data from source
        
    Parameters
    ------------
    source: DataSource
        A source of data
    """
      
   def __init__(self, source: DataSource) - > None:
       
       self.data = list(source.get_data())
    

@jsh9
Copy link
Owner

jsh9 commented Jan 12, 2025

Hi @erklem , your question may be bigger than I can answer.

This is because there is no distinction in the docstring between "class attributes" and "instance attributes" --- there's only the "Attributes" section. (Similarly for the Google style, in which there's only the "Args" section.)

This inherent ambiguity means that we have to make a choice: either use "Attributes" for documenting class attributes, or use it for documenting instance attributes.

I made the choice of former (use "Attributes" for documenting class attributes), because I think this use case is more common.

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

5 participants