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 elementx_i
of the input arrayx
. Each elementx_i
is assumed to be expressed in radians. Special cases For floating-point operands, - Ifx_i
isNaN
, the result isNaN
. - Ifx_i
is+0
, the result is+0
. - Ifx_i
is-0
, the result is-0
. - Ifx_i
is either+infinity
or-infinity
, the result isNaN
. 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 inx
. 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 elementx_i
of the input arrayx
. Each elementx_i
is assumed to be expressed in radians. Special cases For floating-point operands, - Ifx_i
isNaN
, the result isNaN
. - Ifx_i
is+0
, the result is+0
. - Ifx_i
is-0
, the result is-0
. - Ifx_i
is either+infinity
or-infinity
, the result isNaN
. 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 inx
. 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 acceptsivy.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