Chilipp / docrep

A Python Module for intelligent reuse of docstrings
Apache License 2.0
30 stars 4 forks source link

Issue when using docrep with instance methods #25

Closed mario-bermonti closed 3 years ago

mario-bermonti commented 3 years ago

When using docrep to reuse an instance method's docstring, I found that the order of the methods matter. The method from which the docstring is extracted has to be defined before the method that uses its docstring.

I guess it makes sense, but it took me by surprise because when dealing with classes I usually don't think of methods in a linear way (as in procedural programming). I usually think of each method in the API as independent from each other in terms of the user calling it.

Is this intended or just something for which there is no workaround? I would prefer not to move the methods around in the class, but it wouldn't be the end of the world. I usually write the more general method that uses other private methods and just after it I add the methods that are used by the first method.

If it is intended or unavoidable then it should probably be documented.

Some code to illustrate this:

This will work:

from docrep import DocstringProcessor
DocstringProcessor.text_sections = ["Rules", "Notes", "Warnings"]
docstrings = DocstringProcessor()

class W():
    @docstrings.get_sections(base="silent_u", sections=["Parameters", "Rules"])
    @docstrings.dedent
    def _check_silent_u(self):
        """
        Count the number of silent letters *u*.

        Parameters
        ----------
        x : int
            some param

        Rules 
        -----
        *u*: when preceded by a *g* or *q* and
            followed by an *i* or *e*. These follow the pattern *gui*, *gue*,
            *que*, *qui*. 

        Returns
        -------
        silent_u_count: int
            Number of silent letters *u* in the word
        """
        pass

    @docstrings.dedent(4)
    def _check_silent_letters(self):
        """
        Count the number of silent letters.

        Silent letters are graphemes that do not have a phonemic representation.
        In Spanish, silent letters are *h*s and *u*s that meet certain criteria. 

        %(silent_u.parameters)s

        Rules
        -----
        %(silent_u.rules)s

        Returns
        -------
        silent_letter_count : int
            Number of silent letters.
        """
        pass

print(W._check_silent_letters.__doc__)

But this will not work:

from docrep import DocstringProcessor
DocstringProcessor.text_sections = ["Rules", "Notes", "Warnings"]
docstrings = DocstringProcessor()

class W():
    @docstrings.dedent(4)
    def _check_silent_letters(self):
        """
        Count the number of silent letters.

        Silent letters are graphemes that do not have a phonemic representation.
        In Spanish, silent letters are *h*s and *u*s that meet certain criteria. 

        %(silent_u.parameters)s

        Rules
        -----
        %(silent_u.rules)s

        Returns
        -------
        silent_letter_count : int
            Number of silent letters.
        """
        pass

    @docstrings.get_sections(base="silent_u", sections=["Parameters", "Rules"])
    @docstrings.dedent
    def _check_silent_u(self):
        """
        Count the number of silent letters *u*.

        Parameters
        ----------
        x : int
            some param

        Rules 
        -----
        *u*: when preceded by a *g* or *q* and
            followed by an *i* or *e*. These follow the pattern *gui*, *gue*,
            *que*, *qui*. 

        Returns
        -------
        silent_u_count: int
            Number of silent letters *u* in the word
        """
        pass

print(W._check_silent_letters.__doc__)
Chilipp commented 3 years ago

hey @mario-bermonti!

two things here:

  1. @docstrings.dedent(4) is wrong. you can either call dedent with a string (e.g. dedent('something') or use it as a decorator without a function call, i.e.
    @docstrings.dedent
    def myfunc():...
  2. what is causing your issue is yes, the order of the methods is indeed important. In your second call, when you do a
    @docstrings.dedent(4)
    def _check_silent_letters(self):

    the docstring of _check_silent_u has not yet been processed. So it cannot modify the docstring. This is not something specific to docrep, this is specific to how python interpretes your code.

If you really do not want to change the order, you could move the docstrings.dedent call to the end, i.e. something like

from docrep import DocstringProcessor
DocstringProcessor.text_sections = ["Rules", "Notes", "Warnings"]
docstrings = DocstringProcessor()

class W():

    def _check_silent_letters(self):
        """
        Count the number of silent letters.

        Silent letters are graphemes that do not have a phonemic representation.
        In Spanish, silent letters are *h*s and *u*s that meet certain criteria. 

        %(silent_u.parameters)s

        Rules
        -----
        %(silent_u.rules)s

        Returns
        -------
        silent_letter_count : int
            Number of silent letters.
        """
        pass

    @docstrings.get_sections(base="silent_u", sections=["Parameters", "Rules"])
    @docstrings.dedent
    def _check_silent_u(self):
        """
        Count the number of silent letters *u*.

        Parameters
        ----------
        x : int
            some param

        Rules 
        -----
        *u*: when preceded by a *g* or *q* and
            followed by an *i* or *e*. These follow the pattern *gui*, *gue*,
            *que*, *qui*. 

        Returns
        -------
        silent_u_count: int
            Number of silent letters *u* in the word
        """
        pass

    docstrings.dedent(_check_silent_letters)

print(W._check_silent_letters.__doc__)
mario-bermonti commented 3 years ago
  1. You are right. Using docstring.dedent(4) is a typo. I was originally using docstring.with_indent(4) and changed it to dedent to try it with it and forgot to clean the code.
  2. I’m using docstrings.with_indent. Based on the documentation, I understand I could use docstrings.with_indent(_check_silent_letters). Right?

I originally thought that DocstringProcessor was only meant to be used as a decorator. My bad there.

I imagine we can close this now, right?

Chilipp commented 3 years ago

hey @mario-bermonti! Yes, we can close this

Based on the documentation, I understand I could use docstrings.with_indent(_check_silent_letters). Right?

it's rather docstrings.with_indent(_check_silent_letters, 8), but yes

mario-bermonti commented 3 years ago

Great. I’m going to try this. Thank you for all the help.