How to write docstrings#

In this guide, we will explain how to write docstrings for tudat and tudatpy. We will also include a template for documenting enums, classes and (factory) functions.

Note

Before diving into this guide, the user should be familiar with the page about Exposing C++ to Python.

YAML files#

The source of docstrings are located in yaml files in the docstring directory on Github. The content is divided over a file tree structure that mimics the structure of the tudatpy exposure (see this directory on Github), which is the same structure of the tudatpy modules. Each file bundles the content of a module exposure function (i.e. Ephemeris, Gravity Field, Rotation, etc). Within each yaml file, all module classes are listed under a single “classes” key, while functions are listed under a single “functions” key.

Note

For tudatpy-native classes and functions (i.e., not exposed from C++ code but directly coded in Python), the docstrings can be written directly in the Python source files. Only the name of the class and method needs to be included in the yaml file. An example of this can be found here for the docstring and here for the mention in the yaml file.

API Structure Definition#

The generic structure definition of a Python API reference system is provided below:

definition/
├── __api__.yaml
├── module1.yaml
├── module2
│         ├── __module__.yaml
│         └── submodule1.yaml
└── module3
    ├── __module__.yaml
    └── submodule2
           └── subsubmodule.yaml

The building blocks can be broken down into the following elements:

Element

Description

__api__.[yml/yaml]

API configuration file. Must exist in the API structure prefix.

module.[yml/yaml]

Module configuration file. Module definition as a file implicitly infers no submodules.

/module

Module configuration directory. Must contain __module__.[yml/yaml]

submodule.[yml/yaml]

Submodule configuration file. Equivalent to a module configuration file.

yaml files#

YAML files contain the sources of the docstrings and are organized as key-value pairs. An example of a typical YAML file is provided below.

Warning

The example taken from this docstring file but it was heavily adapted to make it shorter, so it does not contain meaningful information.

Show/Hide example

extended_summary: |
   This module provides the functionality for creating integrator settings.

enums:
   - name: AvailableIntegrators
     short_summary: "Enumeration of available integrators."
     extended_summary: |
       Enumeration of integrators supported by tudat.
     members:
       - name: euler # [cpp]
       - name: rungeKutta4 # [cpp]
       - name: euler_type # [py]
       - name: runge_kutta_4_type # [py]

 classes:
   - name: IntegratorSettings
     short_summary: "Functional base class to define settings for integrators."
     extended_summary: |
       Class to define settings for numerical integrators, for instance for use in numerical integration of equations of motion/
       variational equations. This class can be used for simple integrators such as fixed step RK and Euler. Integrators that
       require more settings to define have their own derived class.
     methods:
       - name: ctor # [cpp]
         short_summary: "Constructor." # [cpp]
         extended_summary: "Instances of this class are typically not generated by the user because this is a base class." # [cpp]

     attributes:
       - name: initial_time # [py]
         type: float # [py]
         description: Initial time of the integration. # [py]

 functions:
   # Euler
   - name: eulerSettings # [cpp]
   - name: euler # [py]
     short_summary: "Creates the settings for the Euler integrator."
     extended_summary: |
       Factory function to create settings for the Euler integrator. For this integrator, the step size is kept
       constant.
     parameters:
       - name: initialTime # [cpp]
         type: double # [cpp]
       - name: initial_time # [py]
         type: float # [py]
         description: Start time (independent variable) of numerical integration.

       - name: initialTimeStep # [cpp]
         type: double # [cpp]
       - name: initial_time_step # [py]
         type: float # [py]
         description: Initial and constant value for the time step.

     returns:
         type: IntegratorSettings
         description: Integrator settings object.

As the example shows, the following keys are accepted:

  • extended_summary (for the module)

  • enums

  • classes

  • functions

Each of those sections (except for extended_summary) accepts a number of items. Each item should start with:

- name: "..."

where the dots are replaced by the name of the enum, class, or function.

Note

  • Keys and values entries in YAML files require a leading dash only if they are part of a list.

  • A string can be provided in YAML files through quotation marks or with the | linebreak symbol.

Each item also has different fields. We adopted the numpydoc documentation style. As a result, in our API reference each function or class can accept all the fields specified by numpydoc (see here for an extensive list).

Warning

For enums, as they are not Python native objects, an additional members field is made available.

tudat vs. tudatpy#

Tudat and tudatpy API documentations are generated from the same yaml files.

Tudat-exclusive content is marked by the # [cpp] tag, while tudatpy-exclusive content is marked by # [py].

Note

Untagged content will be included in both API documentations.

Typically, the two APIs convey the same content. That means that the same functions, parameters and returns (etc) are listed in both APIs, where names and types are adopted to the respective API ([cpp] or [py]). Most class or function summaries are the same (word-by-word) for the two APIs.

Documentation style#

The text in the docstring will be parsed and rendered by Sphinx. Therefore, any sphinx command can be used in the yaml files.

Warning

There should be a balance between the readability of the raw docstrings and the intended aesthetical effects provided by Sphinx. Even if most of the users will consult the online API reference, the same docstrings will be also shipped with the tudatpy conda package, so the docstrings can be consulted locally. Docstrings with many Sphinx commands will be difficult to read and interpret.

Below, a few important aspects of the documentation style are outlined.

Factory functions#

See also

All examples from this subsection have been inspired from (but do not correspond exactly to) this file.

Factory functions (FFs) are functions creating instances of objects via the class constructors ) and they are intended to be the user’s interface with the actual class constructors, such that the users typically do not interact with the classes as such. FFs will be used throughout all user guides, examples and tutorials. They will be the user`s landing pad in the API. It is therefore the intention to supply all functionality-related information in the docstrings of the FF. This may include (but is not limited to) complete explanations for function parameters, information about the models (that will be created by the classes), model implementation and links to external resources.

Example

functions:
    # Factory function instantiating an object of type CentralGravityFieldSettings (see next example)
  - name: central # [py]
  - name: centralGravitySettings # [cpp]
    short_summary: "Factory function for central gravity field settings object."
    extended_summary: |
      Factory function for settings object, defining a point-mass gravity field model with user-defined gravitational parameter.
    parameters:
      - name: gravitational_parameter # [py]
        type: float # [py]
      - name: gravitationalParameter # [cpp]
        type: double # [cpp]
        description: Gravitational parameter defining the point-mass gravity field.
    returns:
        type: CentralGravityFieldSettings
        description: Instance of the :class:`~tudatpy.numerical_simulation.environment_setup.gravity_field.GravityFieldSettings` derived :class:`~tudatpy.numerical_simulation.environment_setup.gravity_field.CentralGravityFieldSettings` class

(derived) classes#

Classes, on the other hand, are documented in a more minimalistic manner, focused more on code design and hierarchy and less on the functional aspects. Constructors of classes that have FFs implemented will not be documented with parameters and returns keys, since users are discouraged from directly using the constructor method. short_description of the constructor method will be given by the string "Constructor". extended_description of the constructor method will refer the user to use the respective FF for creating instances of the given class.

Example

classes:
  # Derived class from GravityFieldSettings (see next example)
  - name: CentralGravityFieldSettings
    short_summary: "`GravityFieldSettings` derived class defining settings of point mass gravity field."
    extended_summary: |
      Derived class of `GravityFieldSettings` for central gravity fields, which are defined by a single gravitational parameter.

    methods: # [cpp]
        # Class constructor
      - name: ctor # [cpp]
        short_summary: "Constructor." # [cpp]
        extended_summary: "Instances of the `CentralGravityFieldSettings` class should be created through the `centralGravitySettings` factory function." # [cpp]
        # Class constructor's parameter
      - name: getGravitationalParameter # [cpp]
        short_summary: "Retrieve gravitational parameter." # [cpp]
        extended_summary: "Function to retrieve gravitational parameter of the settings object." # [cpp]
        parameters: # [cpp]
          - name: None # [cpp]
        returns: # [cpp]
            type: double # [cpp]
            description: Gravitational parameter of central gravity field. # [cpp]

Base classes#

Base classes are to be identified as such (in short_description). Typically, users do not create instances of the base classes (but of the derived classes through the dedicated FFs) and this shall also be mentioned in the in the extended_description.

Example

classes:
    # Base class
  - name: GravityFieldSettings
    short_summary: "Base class for providing settings for automatic gravity field model creation."
    extended_summary: |
      This class is a functional base class for settings of gravity field models that require no information in addition to their type.
      Gravity field model classes requiring additional information must be created using an object derived from this class.

    properties: # [py]
      - name: gravity_field_type # [py]
        type: GravityFieldType # [py]
        description: Type of gravity field model that is to be created. # [py]
        readonly: True # [py]

    methods:
      - name: __init__ # [py]
      - name: ctor # [cpp]
        short_summary: "Constructor." # [cpp]
        extended_summary: "Instances of this class are typically not generated by the user. Settings objects for gravity field models should be instantiated through the factory functions of a derived class." # [cpp]

Python properties vs. C++ getters/setters#

An exception to the analogous structure of the two APIs is the treatment of class attributes.

The original get/set methods of the tudat classes are exposed as “properties” in tudatpy classes (see our guide about Class attributes in C++ vs. in Python).

As a result, class attributes are only documented as such for the tudatpy API, while the get/set methods of the classes are documented in the tudat API instead.

Example

classes:
    # Derived class
  - name: CentralGravityFieldSettings
    short_summary: "`GravityFieldSettings` derived class defining settings of point mass gravity field."
    extended_summary: |
      Derived class of `GravityFieldSettings` for central gravity fields, which are defined by a single gravitational parameter.

    # Properties (only for Python)
    properties: # [py]
      - name: gravitational_parameter # [py]
        type: float # [py]
        description: Gravitational parameter of central gravity field. # [py]

    methods: # [cpp]
      - name: ctor # [cpp]
        short_summary: "Constructor." # [cpp]
        extended_summary: "Instances of the `CentralGravityFieldSettings` class should be created through the `centralGravitySettings` factory function." # [cpp]

        # Getter (only for C++)
      - name: getGravitationalParameter # [cpp]
        short_summary: "Retrieve gravitational parameter." # [cpp]
        extended_summary: "Function to retrieve gravitational parameter of the settings object." # [cpp]
        parameters: # [cpp]
          - name: None # [cpp]
        returns: # [cpp]
            type: double # [cpp]
            description: Gravitational parameter of central gravity field. # [cpp]

        # Setter (only for C++)
      - name: resetGravitationalParameter # [cpp]
        short_summary: "Reset gravitational parameter." # [cpp]
        extended_summary: "Function to reset gravitational parameter of the settings object." # [cpp]
        parameters: # [cpp]
          - name: gravitationalParameter # [cpp]
            type: double # [cpp]
            description: Gravitational parameter of central gravity field that is to be defined by the settings object. # [cpp]

Docstring template#

As an additional resource, we have assembled a template to kickstart the writing process of docstrings. It can be found in YAML templates.