Docstrings#

All functions in the Ivy API at ivy/functional/ivy/category_name.py should have full and thorough docstrings. In contrast, all backend implementations at ivy/functional/backends/backend_name/category_name.py should not have any docstrings, on account that these are effectively just different instantiations of the functions at ivy/functional/ivy/category_name.py.

In order to explain how docstrings should be written, we will use ivy.tan() as an example.

Firstly, if the function exists in the Array API Standard, then we start with the corresponding docstring as a template. These docstrings can be found under spec/API_specification/array_api.

Important: you should open the file in raw format. If you copy directly from the file preview on GitHub before clicking raw, then the newlines will not be copied over, and the docstring will render incorrectly in the online docs.

The Array API Standard docstring for tan is as follows:

Calculates an implementation-dependent approximation to the tangent, having domain (-infinity, +infinity) and codomain (-infinity, +infinity), for each element x_i of the input array x. Each element x_i is assumed to be expressed in radians.

Special cases

For floating-point operands,

- If x_i is NaN, the result is NaN.
- If x_i is +0, the result is +0.
- If x_i is -0, the result is -0.
- If x_i is either +infinity or -infinity, the result is NaN.

Parameters
----------
x: array
    input array whose elements are expressed in radians. Should have a floating-point data type.

Returns
-------
out: array
    an array containing the tangent of each element in x. The returned array must have a floating-point data type determined by type-promotion.

This is a good starting point. But we need to make some changes. Firstly, given that we are using type hints, repeating all of the types also in the docs would be a needless duplication. Therefore, we simply remove all type info from the docstring like so:

-x: array
+x
-out: array
+out

The Array API Standard defines a subset of behaviour that each function must adhere to. Ivy extends many of these functions with additional behaviour and arguments. In the case of ivy.tan(), there is also the argument out which needs to be added to the docstring, like so:

+out
+    optional output array, for writing the result to. It must have a shape that the inputs
+    broadcast to.

Because of this out argument in the input, we also need to rename the out argument in the return, which is the default name used in the Array API Standard. We change this to ret:

-out
+ret

Next, we add a section in the docstring which explains that it has been modified from the version available in the Array API Standard:

+This function conforms to the `Array API Standard
+<https://data-apis.org/array-api/latest/>`_. This docstring is an extension of the
+`docstring <https://data-apis.org/array-api/latest/API_specification/generated/array_api.tan.html>`_
+in the standard.

Finally, if the function is nestable, then we add a simple explanation for this as follows:

+Both the description and the type hints above assumes an array input for simplicity,
+but this function is *nestable*, and therefore also accepts :class:`ivy.Container`
+instances in place of any of the arguments.

Following these changes, the new docstring is as follows:

Calculates an implementation-dependent approximation to the tangent, having
domain (-infinity, +infinity) and codomain (-infinity, +infinity), for each
element x_i of the input array x. Each element x_i is assumed to be
expressed in radians.

Special cases

For floating-point operands,

- If x_i is NaN, the result is NaN.
- If x_i is +0, the result is +0.
- If x_i is -0, the result is -0.
- If x_i is either +infinity or -infinity, the result is NaN.

Parameters
----------
x
    input array whose elements are expressed in radians. Should have a
    floating-point data type.
out
    optional output array, for writing the result to. It must have a shape that the inputs
    broadcast to.

Returns
-------
ret
    an array containing the tangent of each element in x. The return must have a
    floating-point data type determined by type-promotion.

This function conforms to the Array API Standard. This docstring is an extension of the
docstring
in the standard.

Both the description and the type hints above assumes an array input for simplicity,
but this function is nestable, and therefore also accepts ivy.Container
instances in place of any of the arguments.

If the function that you are writing a docstring for is not in the Array API Standard, then you must simply follow this general template as closely as possible, but instead you must use your own judgment when adding descriptions for the overall function, and also for each of its arguments.

Classes

The instance methods in ivy.Array and ivy.Container which directly wrap a function in the functional API do not require thorough docstrings, on account that these instance methods require no explanation beyond that provided in the docstring for the wrapped function.

Therefore, these docstrings should all simply contain the following text:

ivy.<Array|Container> <instance|special|reverse special> method variant of ivy.<func_name>. This method simply wraps the
function, and so the docstring for ivy.<func_name> also applies to this method
with minimal changes.

Parameters
----------
<parameters with their description>

Returns
-------
<return value with its description>

The exception to this is ivy.Container special method docstrings, which should instead use the following text, as these do not directly wrap a function in Ivy’s functional API, but rather wrap the pure operator functions themselves, which can be called on any types that support the corresponding special methods:

ivy.Container <special|reverse special> method for the <operator_name> operator,
calling operator.<operator_name> for each of the corresponding leaves of
the two containers.

Parameters
----------
<parameters with their description>

Returns
-------
<return value with its description>

Let’s take ivy.add() as an example. The docstring for ivy.add is thorough, as explained above. However, the docstrings for ivy.Array.add, ivy.Container.add all follow the succinct pattern outlined above. Likewise, the docstrings for the special methods ivy.Array.__add__, ivy.Array.__radd__, ivy.Container.__add__, and ivy.Container.__radd__, also follow the succinct pattern outlined above. Note that these docstrings all also include examples, which we will cover in the next section.

For all other classes, such as the various layers at ivy/ivy/stateful/layers, then we should add full and thorough docstrings for both the constructor and also all methods.

This is the case even when the class directly wraps a function in the functional API. For example, the class ivy.Linear wraps the function ivy.linear, but does so in a stateful manner with the variables stored internally in the instance of the class. Even though the ivy.Linear class wraps ivy.linear() in the forward pass defined in ivy.Linear._forward, the function signatures of ivy.linear() and ivy.Linear._forward() are still quite distinct, with the former including all trainable variables explicitly, and the latter having these implicit as internal instance attributes of the class.

Therefore, with the exception of the ivy.Array and ivy.Container methods which directly wrap functions in the functional API, we should always add full and thorough docstrings to all methods of all other classes in Ivy, including cases where these also directly wrap functions in the functional API.

Round Up

These examples should hopefully give you a good understanding of what is required when adding docstings.

If you have any questions, please feel free to reach out on discord in the docstrings thread!

Video