Decorator for parametric functions.

    The decorated function is always called under
    a parameter scope ``scope_name``.
    Also, the decorator adds an additional argument ``name`` (:obj:`str`,
    default is ``None``) at the end. If ``name`` is specified, the
    scope ``scope_name`` comes under a scope ``name``. This feature
    could reduce vertical space usage of the source code.
    Any parametric function should be decorated by this.

    Args:
        scope_name (str, optional): The original function will be called
            under a parameter scope named by ``scope_name``.
        param_desc (list, optional):
            Descriptions of parameters will be automatically included into docstring.
            This must be a list of tuples with 4 elements composed of
            (name (str), description (str), shape info (str), need_grad (bool)).

    Returns:
        function: A decorated parametric function.

def parametric_function_api(scope_name=None, param_desc=None):
    """Decorator for parametric functions.

    The decorated function is always called under
    a parameter scope ``scope_name``.
    Also, the decorator adds an additional argument ``name`` (:obj:`str`,
    default is ``None``) at the end. If ``name`` is specified, the
    scope ``scope_name`` comes under a scope ``name``. This feature
    could reduce vertical space usage of the source code.
    Any parametric function should be decorated by this.

    Args:
        scope_name (str, optional): The original function will be called
            under a parameter scope named by ``scope_name``.
        param_desc (list, optional):
            Descriptions of parameters will be automatically included into docstring.
            This must be a list of tuples with 4 elements composed of
            (name (str), description (str), shape info (str), need_grad (bool)).

    Returns:
        function: A decorated parametric function.

    """
    if scope_name is None:
        scope_name = name

    def parametric_function_api_inside(func):
        from nnabla.utils.py23_compatible import getargspec
        import inspect

        name = func.__name__
        doc = func.__doc__

        if param_desc:
            indent = 8
            try:
                desc = map(lambda d: ' ' * indent +
                           '* {} (``need_grad={}``) : {}. (shape: ``{}``)'.format(d[0], d[3], d[1], d[2]), param_desc)
            except:
                ValueError(
                    'param_desc argument of parametric_function_api must be '
                    'None or a list of tuple with three elements composed of '
                    '(name(str), description(str), need_grad(bool)).')
            doc += '''
    Parameters to be registered
        The following variables are registered in a parameter scope ``"{}"``;

{}

            '''.format(scope_name, '\n'.join(desc))

        doc += """
    Note:

        If the ``name`` option is passed, the parameters become wrapped inside the parameter scope
        with the specified name, yielding the same results as the following code.
        This can be used to simplify the code.

        .. code-block:: python

            with parametric_scope(name):
                output = {name}(<args>)

        """.format(name=name)

        spec = getargspec(func)
        defaults = spec.defaults
        if defaults is None:
            defaults = tuple()  # None will be appended later
        signature = inspect.formatargspec(
            spec.args + ['name'],
            spec.varargs, spec.keywords,
            defaults + (None,))
        shortsignature = inspect.formatargspec(
            spec.args, spec.varargs, spec.keywords, None)

        # Check required argument
        assert 'fix_parameters' in spec.args, \
            "A parametric function must take `fix_parameters` as an argument." \
            " `{}{}` doesn't have it.".format(name, signature)

        code = """
def {name}{signature}:
    if name is None:
        with parameter_scope(scope_name):
            return func{shortsignature}
    with parameter_scope(name):
        with parameter_scope(scope_name):
            return func{shortsignature}
        """.format(**locals())
        execdict = dict(
            func=func, parameter_scope=nn.parameter_scope, scope_name=scope_name)
        exec_(code, execdict)
        newfunc = execdict[name]
        newfunc.__doc__ = doc
        newfunc.__parametric_function_api_base__ = func
        newfunc.__scope_name__ = scope_name
        newfunc.__module__ = __name__
        return newfunc
    return parametric_function_api_inside