Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?

Python Documentation With Docstrings and Sphinx

Within the final installment of the collection we checked out how one can obtain testing in our python tasks. Very like testing, documentation can go a great distance in the direction of reaching adoption in your challenge. On this case we’ll be taking a look at how one can generate documentation of your code utilizing docstrings and sphinx.



pylint Prep

When doing the linting I explicitly disabled checking for docstrings. Now that we’ll be utilizing them, it is time to allow that examine within the pyproject.toml file. First nevertheless, we’ll need to examine what the present state of our code is:

> pdm run pylint --recursive=y .
************* Module src.my_pdm_project.mymath
srcmy_pdm_projectmymath.py:10:4: R1720: Pointless "elif" after "increase", take away the main "el" from "elif" (no-else-raise)
srcmy_pdm_projectmymath.py:16:10: W3101: Lacking timeout argument for technique 'requests.get' could cause your program to hold indefinitely (missing-timeout)
srcmy_pdm_projectmymath.py:20:4: R1705: Pointless "else" after "return", take away the "else" and de-indent the code inside it (no-else-return)
************* Module assessments.test_mymath
teststest_mymath.py:23:11: C0123: Use isinstance() quite than kind() for a typecheck. (unidiomatic-typecheck)
teststest_mymath.py:24:11: C0123: Use isinstance() quite than kind() for a typecheck. (unidiomatic-typecheck)

------------------------------------------------------------------
Your code has been rated at 9.25/10 (earlier run: 9.25/10, +0.00)
Enter fullscreen mode

Exit fullscreen mode

So we see how the final growth course of works of writing code, checking with linting, and fixing issues that come up. So first off is that this part:

    if operation == "https://style-tricks.com/" and b == 0:
        increase ZeroDivisionError
    elif operation not in SUPPORTED_OPERATIONS:
        increase ValueError
Enter fullscreen mode

Exit fullscreen mode

That is fairly easy, it simply needs an if used as an alternative of elif resulting from how exceptions deal with breaks of logic circulation. I am going to go forward and replace it right here:

    if operation == "https://style-tricks.com/" and b == 0:
        increase ZeroDivisionError
    if operation not in SUPPORTED_OPERATIONS:
        increase ValueError
Enter fullscreen mode

Exit fullscreen mode

The following is that requests is getting used with out setting a timeout worth. If the server was not responsive then our connection may find yourself in a caught state. I am going to go forward and repair that right here:

    res = requests.get(
        f'{BASE_URI}{operation_expression}', timeout=20
    )
Enter fullscreen mode

Exit fullscreen mode

This may throw an error if a response just isn’t acquired inside 20 seconds. Subsequent is that the else right here is redundant:

    if operation == "https://style-tricks.com/":
        return float(res.textual content)
    else:
        return int(res.textual content)
Enter fullscreen mode

Exit fullscreen mode

I am going to go forward and replace that right here:

    if operation == "https://style-tricks.com/":
        return float(res.textual content)
    return int(res.textual content)
Enter fullscreen mode

Exit fullscreen mode

Lastly in our assessments we’re utilizing kind() as an alternative of isinstance() which is cleaner. I am going to go forward and replace the assessments to do this:

    assert consequence == 5
    assert isinstance(consequence, int)
    assert isinstance(result2, float)
Enter fullscreen mode

Exit fullscreen mode

Now that every part is cleaned up I am going to go forward and run pylint once more:

> pdm run pylint --recursive=y .

-------------------------------------------------------------------
Your code has been rated at 10.00/10 (earlier run: 9.25/10, +0.75)
Enter fullscreen mode

Exit fullscreen mode

Now it is time to allow the docstring examine. I am going to go forward and do that by eradicating the next portion from pyproject.toml:

[tool.pylint."MESSAGES CONTROL"]
disable = '''
missing-module-docstring,
missing-class-docstring,
missing-function-docstring
'''
Enter fullscreen mode

Exit fullscreen mode

Now I am going to run pylint once more:

> pdm run pylint --recursive=y .
************* Module src.my_pdm_project.mymath
srcmy_pdm_projectmymath.py:1:0: C0114: Lacking module docstring (missing-module-docstring)
srcmy_pdm_projectmymath.py:9:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:25:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:29:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:33:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:37:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:41:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
************* Module assessments.test_mymath
teststest_mymath.py:1:0: C0114: Lacking module docstring (missing-module-docstring)
teststest_mymath.py:16:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:27:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:32:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:39:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:46:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:52:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:62:0: C0116: Lacking operate or technique docstring (missing-function-docstring)

-------------------------------------------------------------------
Your code has been rated at 7.61/10 (earlier run: 10.00/10, -2.39)
Enter fullscreen mode

Exit fullscreen mode

In order you possibly can see there’s plenty of output. Now let’s discuss how one can go about fixing this.



Docstrings

Docstrings are accomplished by enclosing textual content in triple double quotes. For instance:

def add_numbers(a: int, b: int):
    """
    That is my operate
    """
    return make_mathjs_request(a, b, '+')
Enter fullscreen mode

Exit fullscreen mode

Now the official docstring specification is a part of PEP 257. Whereas it does describe the general format it is not particular in regards to the format of what you’ll put in a doc string. On this case I will be using sphinx because the code documentation generator of selection. Now let us take a look at what our operate’s doc string will develop into utilizing the sphinx format:

def add_numbers(a: int, b: int):
    """Add two numbers collectively

    :param a: The bottom integer to make use of within the add operation
    :kind a: int
    :param b: The integer so as to add to the bottom integer
    :kind b: int

    :return: The sum of each integers
    :rtype: int
    """
Enter fullscreen mode

Exit fullscreen mode

:param: signifies what a parameter is supposed for and :kind: signifies the kind of the parameter. :return: will describe the return of the operate and :rtype: the return worth. Now after implementing this docstring and working pylint once more:

> pdm run pylint --recursive=y .
************* Module src.my_pdm_project.mymath
srcmy_pdm_projectmymath.py:1:0: C0114: Lacking module docstring (missing-module-docstring)
srcmy_pdm_projectmymath.py:9:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:39:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:43:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:47:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:51:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
************* Module assessments.test_mymath
teststest_mymath.py:1:0: C0114: Lacking module docstring (missing-module-docstring)
teststest_mymath.py:16:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:27:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:32:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:39:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:46:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:52:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
teststest_mymath.py:62:0: C0116: Lacking operate or technique docstring (missing-function-docstring)

------------------------------------------------------------------
Your code has been rated at 7.76/10 (earlier run: 7.61/10, +0.15)
Enter fullscreen mode

Exit fullscreen mode

We are able to see there’s a slight enhance in our general rating since we added the docstring. Now one concern right here is that pylint is anticipating the assessments to have docstrings. Performance smart we actually do not want them in our assessments for the reason that level of code documentation is to indicate the customers how the code they’re consuming works. A normal person is not going to be consuming assessments. On the high of the take a look at file I can inform pylint to disregard docstrings for them since I nonetheless need it to verify the opposite elements of my assessments are strong:

# pylint: disable=missing-docstring
from urllib.parse import quote_plus
import pytest
import requests_mock

from my_pdm_project.mymath import (
    add_numbers,
Enter fullscreen mode

Exit fullscreen mode

Now after working pylint:

> pdm run pylint --recursive=y .
************* Module src.my_pdm_project.mymath
srcmy_pdm_projectmymath.py:1:0: C0114: Lacking module docstring (missing-module-docstring)
srcmy_pdm_projectmymath.py:9:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:39:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:43:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:47:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:51:0: C0116: Lacking operate or technique docstring (missing-function-docstring)

------------------------------------------------------------------
Your code has been rated at 9.05/10 (earlier run: 8.96/10, +0.09)
Enter fullscreen mode

Exit fullscreen mode

So I am a giant nearer right here. Now apart from the features it is also mentioning a module docstring. On the high of our python code we are able to merely write an outline of what the underlying code is supposed to do:

"""
A module containing basic math operations.
"""
from urllib.parse import quote_plus
import numpy as np
import requests
Enter fullscreen mode

Exit fullscreen mode

Now one other examine:

> pdm run pylint --recursive=y .
************* Module my_pdm_project.mymath
srcmy_pdm_projectmymath.py:12:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:42:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:46:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:50:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:54:0: C0116: Lacking operate or technique docstring (missing-function-docstring)

------------------------------------------------------------------
Your code has been rated at 9.21/10 (earlier run: 9.05/10, +0.16)
Enter fullscreen mode

Exit fullscreen mode

So now simply the operate docstrings are left. Let’s take a look at a extra complicated instance for make_mathjs_request:

def make_mathjs_request(a: int, b: int, operation: str):
    """Make a expression name towards the MathJS API

    :param a: Base integer for the operation
    :kind a: int
    :param b: Integer to make use of with a within the operation
    :kind b: int
    :param operation: Operation to run towards a and b
    :kind operation: str

    :raises ZeroDivisionError: Raised if division by 0
    :raises ValueError: Raised if not a supported operation

    :returns:
        - int for non division operations
        - float for division operations
    """

Enter fullscreen mode

Exit fullscreen mode

Right here it is considerably like what we noticed earlier than. What’s new now could be that we additionally documentation exceptions that may be raised, and why they’d be raised. The :returns: is used since we’re returning both a float or int relying on the operation. As talked about earlier than this was accomplished for example case for displaying testing and usually you would not need a operate to return a number of sorts. After a pylint run:

> pdm run pylint --recursive=y .
************* Module my_pdm_project.mymath
srcmy_pdm_projectmymath.py:58:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:62:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:66:0: C0116: Lacking operate or technique docstring (missing-function-docstring)
srcmy_pdm_projectmymath.py:70:0: C0116: Lacking operate or technique docstring (missing-function-docstring)

------------------------------------------------------------------
Your code has been rated at 9.37/10 (earlier run: 9.21/10, +0.16)
Enter fullscreen mode

Exit fullscreen mode

So I simply want so as to add docstrings to the opposite features. In any case is completed the file appears like this:

"""
A module containing basic math operations.
"""
from urllib.parse import quote_plus
import numpy as np
import requests

BASE_URI = "http://api.mathjs.org/v4/?expr="
SUPPORTED_OPERATIONS = ['+', '-', '*', "https://style-tricks.com/"]


def make_mathjs_request(a: int, b: int, operation: str):
    """Make a expression name towards the MathJS API

    :param a: Base integer for the operation
    :kind a: int
    :param b: Integer to make use of with a within the operation
    :kind b: int
    :param operation: Operation to run towards a and b
    :kind operation: str

    :raises ZeroDivisionError: Raised if division by 0
    :raises ValueError: Raised if not a supported operation

    :returns:
        - int for non division operations
        - float for division operations
    """
    if operation == "https://style-tricks.com/" and b == 0:
        increase ZeroDivisionError
    if operation not in SUPPORTED_OPERATIONS:
        increase ValueError

    operation_expression = quote_plus(f'{a}{operation}{b}')
    res = requests.get(
        f'{BASE_URI}{operation_expression}', timeout=20
    )

    if operation == "https://style-tricks.com/":
        return float(res.textual content)
    return int(res.textual content)


def add_numbers(a: int, b: int):
    """Add two numbers collectively

    :param a: The bottom integer to make use of within the add operation
    :kind a: int
    :param b: The integer so as to add to the bottom integer
    :kind b: int

    :return: The sum of each integers
    :rtype: int
    """
    return make_mathjs_request(a, b, '+')


def subtract_numbers(a: int, b: int):
    """Subtract two numbers

    :param a: The bottom integer to make use of within the subtract operation
    :kind a: int
    :param b: The integer to subtract the bottom integer from
    :kind b: int

    :return: The subtraction of each numbers
    :rtype: int
    """
    return make_mathjs_request(a, b, '-')


def multiply_numbers(a: int, b: int):
    """A number of two numbers collectively

    :param a: The bottom integer to make use of within the multiply operation
    :kind a: int
    :param b: The integer to multiply towards the bottom quantity
    :kind b: int

    :return: The results of multiplying each numbers
    :rtype: int
    """
    return make_mathjs_request(a, b, '*')


def divide_numbers(a: int, b: int):
    """Divide two numbers

    :param a: The bottom integer to make use of within the add operation
    :kind a: int
    :param b: The integer divide a by
    :kind b: int

    :return: The quotient of the division operation
    :rtype: float
    """
    return make_mathjs_request(a, b, "https://style-tricks.com/")


def average_numbers(numbers: listing[int]):
    """Common a listing of numbers

    :param numbers: The listing of numbers to common
    :kind numbers: listing[int]

    :return: The typical of the numbers
    :rtype: float
    """
    return np.common(numbers)

Enter fullscreen mode

Exit fullscreen mode

After ending up documenting every part right here we are able to examine what pylint has to say:

> pdm run pylint --recursive=y .

-------------------------------------------------------------------
Your code has been rated at 10.00/10 (earlier run: 9.37/10, +0.63)
Enter fullscreen mode

Exit fullscreen mode

All the things is within the clear now!



Documentation Era With sphinx

Now as is the code documentation is a bit tough to work with for a median person. To assist with this we are able to use sphinx to take our docstrings and generate them into numerous codecs. As with our different instruments this will probably be added as a growth bundle:

> pdm add -dG dev sphinx
Enter fullscreen mode

Exit fullscreen mode

Now we’ll must create a doc listing for the place our documentation will probably be saved.

> mkdir docs
Enter fullscreen mode

Exit fullscreen mode

Now it is time to setup sphinx to generate documentation. The factor to remember with sphinx is it is primarily a documentation technology instrument and technology of library documentation is a facet bonus. With this in thoughts we’ll go forward and setup how our challenge will work vi the good sphinx-quickstart utility:

> cd docs
> pdm run sphinx-quickstart --no-makefile -M --ext-autodoc -p "my-pdm-project" -a "Chris White" -v "0.3.0" -r "0.3.0" -l "en" --sep .
Enter fullscreen mode

Exit fullscreen mode

So there’s a couple of issues to digest right here. --no-makefile and -M are accomplished to keep away from utilizing make for constructing. This was principally to keep away from including in one other factor to put in. -p units the title of the challenge, -a the creator, -v and -r are for model and launch. They’re the identical proper now as a result of there isn’t any 1.0 launch but, but when there was I would advocate one thing like 1.0 for the model and 1.0.0 for the discharge. It is considerably like how there’s 3.12 for python however the 3.12 model has a number of releases beneath it. -l units the language of the challenge and --sep ensures that supply and construct directories are separate. I are likely to choose this as a result of it is simpler to disregard the construct listing via issues like .gitignore in a while. Lastly . signifies the listing of the challenge, or extra particularly the “documentation challenge” (as speculated to the code challenge the place all our code is). Now that every part is setup we are able to use sphinx-build to generate html for us:

> pdm run sphinx-build supply/ construct/
Enter fullscreen mode

Exit fullscreen mode

Now if I look within the construct listing there will probably be an index.html I can entry by way of a browser:

Proper now there is not a lot occurring and “Module Index” would not work as a result of it hasn’t been setup to acknowledge our docstrings. To do that we’ll create a brand new file in docs/supply/ referred to as mymath.rst with the next content material:

Mymath Module Documentation
===========================
.. automodule:: my_pdm_project.mymath
    :members:
Enter fullscreen mode

Exit fullscreen mode

Now this format is called rst or reStructuredText. It is a format that is extra function wealthy than markdown and helpful for structured documentation. On this case it is referring to a operate in rst. These features are within the kind:

.. function_name:: arguments
    :possibility: worth

    content material
Enter fullscreen mode

Exit fullscreen mode

Within the case of automodule it should generate documentation for my_pdm_project.mymath together with all of its members. Now in the identical listing there will probably be an index.rst file that must be edited like so:

.. my-pdm-project documentation grasp file, created by
   sphinx-quickstart on Fri Nov 10 09:01:15 2023.
   You may adapt this file utterly to your liking, nevertheless it ought to at the very least
   comprise the basis `toctree` directive.

Welcome to my-pdm-project's documentation!
==========================================
.. toctree::
   :maxdepth: 2
   :caption: Contents:

   mymath


Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
Enter fullscreen mode

Exit fullscreen mode

What’s modified is that mymath has now been added to our desk of contents. Observe that contents are indicated by a clean line after the choices. On this case the content material is mymath on a single line. sphinx is aware of that that is referencing mymath.rst and toctree will robotically parse mymath.rst to supply a desk of contents. Now after working this once more within the docs listing:

> pdm run sphinx-build supply/ construct/
Enter fullscreen mode

Exit fullscreen mode

The principle index.html web page will present:

Image showing a detailed view of the module documentation

And clicking on “Mymath Module Documentation” will present the documentation generated by way of our doc strings:

Image showing the module table of contents on the main index.html page



Conclusion

This concludes a take a look at documentation technology by way of sphinx parsing python docstrings. I’ll say that rst is extra concerned than easy markdown, nevertheless it’s function wealthy nature makes it splendid for a lot of types of documentation construction. Within the subsequent part we’ll be taking a look at orchestrating all of our instruments to this point and importing our code for everybody to make use of.

Add a Comment

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?