Is there a standard format for documenting function-type parameters in Sphinx using sphinx.ext.napoleon?

1.1k Views Asked by At

I am using Sphinx to document one of my projects and one of the classes takes in a function as a parameter in its __init__. Is there a standard way to document this function-type parameter? I am also using sphinx.ext.napoleon to use Google formatting for my docstrings.

Here's an example:

class ExampleClass:
    """An example class to demonstrate my question
    
    Args:
        func (what goes here?): Description of the parameter
    """

    def __init__(self, func):
        self.func = func

    def do_something(self, param):
        """An example method

        Args:
            param (str): Description of param
        
        Returns:
            bool: The return description
        """

        return self.func(param)

In my code, the parameter in question should take in one str and return a bool. Is there a standard way to document this when using Sphinx?

1

There are 1 best solutions below

0
On BEST ANSWER

The simplest way to document the function parameter is using typing.Callable. By doing so the static type checker will verify that the signature (arguments and return type) of the passed function is compatible with the type hint.

from typing import Callable

class ExampleClassTwo:
    """An example class to demonstrate my question.

    Args:
        func (Callable[[str], bool]): Description of the parameter.
    """
    def __init__(self, func: Callable[[str], bool]):

        self.func = func

However, this has one potential problem. If you look at the Python Data Model, in the sub-section titled "Callable types" under 3.2 The standard type hierarchy. You'll notice there are several possible types that are callables, and any callable with the specified signature would not cause a warning from the static type checker. For example, a class instance implementing the __call__() method would not cause a warning:

def str_function(param: str) -> bool:
    pass

class OneInstance:

    def __init__(self, param):
        pass

    def __call__(self, param: str) -> bool:
        pass

one_instance = OneInstance("test instance")
# neither of the below raise a static type check warning
one = ExampleClassTwo(str_function)
two = ExampleClassTwo(one_instance)

You can type hint the param signature as shown above together with validating that param is of type FunctionType in the __init__ as you would validate other parameters at run-time, and raise a TypeError exception if the parameter is not a function.

from typing import Callable
from types import FunctionType

class ExampleClassTwo:
    """An example class to demonstrate my question

    Args:
        func (Callable[[str], bool]): Description of the parameter
    Raises:
        TypeError: Argument `param` is not of type ``FunctionType``.
    """

    def __init__(self, func: Callable[[str], bool]):

        if type(func) in (FunctionType,):
            self.func = func
        else:
            raise TypeError("param must be initialized as being of ``FunctionType``.")

It may be possible to combine both requirements Callable[[str], bool] and FunctionType as an intersection using structural subtyping, I have not yet tried that approach.

Finally some examples are included that would cause the static type checker to raise warnings:

def int_function(param: int) -> bool:
    pass


class ExampleClass:
    """An example class to demonstrate my question

    Args:
        func (FunctionType): Description of the parameter
    """

    def __init__(self, func: FunctionType):
        self.func = func

three = ExampleClass(str_function("test string"))  # Expected type 'FunctionType', got 'bool' instead
four = ExampleClass(str_function)  # Expected type 'FunctionType', got '(param: str) -> bool' instead
five = ExampleClass(type(str_function))  # No warning five.func is {type} <class 'function'>

six = ExampleClassTwo(int_function(2))  # Expected type '(str) -> bool', got 'bool' instead
seven = ExampleClassTwo(str_function("test string"))  # Expected type '(str) -> bool', got 'bool' instead
eight = ExampleClassTwo(int_function)  # Expected type '(str) -> bool', got '(param: int) -> bool' instead
nine = ExampleClassTwo(str_function)  # No warning

enter image description here