gin-config icon indicating copy to clipboard operation
gin-config copied to clipboard

automatically generate a documentation of all configurables?

Open patzm opened this issue 2 years ago • 1 comments

Hi guys,

First of all, I really like your work! 👏. I have a question regarding documentation and transparency. If I have multiple places in my code that use @gin.configurable, can I somehow aggregate all those places and maybe even generate (Sphinx) documentation out of it?

Example:

@gin.configurable
def fun_a(param_a: str, param_b: bool = False, param_c: Sequence[int] = (1, 2, 3)):
    """some docstring that even describes the parameters"""
    pass

@gin.configurable
def fun_b(param_a: float = 1.0):
    pass

Can I get output like

Configurables:

fun_a
    param_a: string
        I am parameter A.
    param_b: bool, default `False`
        I am parameter B.
    param_c: Sequence of integers, default `(1, 2, 3)`
        I am parameter A.

fun_b
    param_a: float, default 1.0
        I am parameter A.

maybe even in machine readable format that anything could read and further process.

patzm avatar Jun 29 '22 14:06 patzm

As a step on the way to this, these are the functions I currently use to get all gin parameters:

def get_all_gin_configurable_signatures() -> dict[str, inspect.Signature]:
    """Get the signatures for all things that are gin-configurable before gin has been applied.

    Returns
    -------
    A dictionary mapping, for each configurable, name (including full module path) to a dictionary mapping parameter
    name to default value. Example: If we only have the L1Loss configurable, we can expect

    .. code-block:: python

        {"torch.nn.L1Loss": {"size_average": None, "reduce": None, "reduction": "mean"}}

    as a result. Parameters might be :class:`inspect.Parameter.empty`.
    """
    gin_configurables = {}
    for name, selectable in gin.config._REGISTRY._selector_map.items():
        gin_configurables[name] = inspect.signature(selectable.wrapped)
    return gin_configurables


def get_all_gin_parameters() -> dict[tuple[str, str], Any]:
    """Get parameters for all things that are gin-configurable after gin-settings have been applied.

    Returns
    -------
    A dictionary mapping, for each configurable, name (including full module path) to a dictionary mapping parameter
    name to default value. If multiple scopes are defined, we return multiple entries. This includes configurables that
    have not been set by gin.

    Example: If we have the L1Loss configurable and have it defined with different parameters in the scope
    `surface_head`, we can expect

    .. code-block:: python

    {
        ("", "torch.nn.L1Loss"):              {"size_average": None, "reduce": None, "reduction": "mean"},
        ("surface_head", "torch.nn.L1Loss"):  {"size_average": None, "reduce": None, "reduction": "sum"}
    }

    as a result. Parameters might be :class:`inspect.Parameter.empty`.

    Does not currently resolve macros or class definitions.
    """
    gin_signatures = get_all_gin_configurable_signatures()
    gin_configurables: dict[tuple[str, str], Any] = {}
    empty_scope = ""
    for name, signature in gin_signatures.items():
        # Add the default parameters with an empty scope.
        gin_configurables[(empty_scope, name)] = {k: v.default for k, v in signature.parameters.items()}
    for (scope, name), param_dict in get_gin_set_params().items():
        gin_configurables[(scope, name)] = copy.deepcopy(gin_configurables[(empty_scope, name)])
        for param_name, new_val in param_dict.items():
            gin_configurables[(scope, name)][param_name] = new_val
    return gin_configurables


def get_gin_set_params() -> dict[tuple[str, str], Any]:
    """Get parameters for all things that gin is modifying.

    Returns
    -------
    A dictionary mapping, for each configurable, a tuple of (scope, name) mapped to the parameter it is set to.
    scope is an empty string if this applies to everything.
    """
    return gin.config._CONFIG

fgerzer avatar Jul 04 '22 06:07 fgerzer