Decorate a function to be the entry function of commands.

    Arguments:
        commands: Names of command that should trigger this function object.
        visible: This command is visible in tab completions.
        internal: The lexing rule is unchanged even if the parse_line() method
            is overloaded in the subclasses.
        nargs: Short for number of arguments. Similar to the nargs argument in
            the argparse module. Has the following valid values:
                    a non-negative integer
                    a list/set/tuple/range of non-negative integers
                    '*':        zero or more
                    '?':        zero or one
                    '+':        one or more
            The command method, whose interface is described below, will check
            the number of arguments. If it does not match this nargs argument,
            an error message will be printed to self.stderr and the shell is
            resumed.

    ----------------------------
    Interface of command methods:

        @command('foo', 'bar')
        def bar(self, args):
            '''The method invoked by 'foo' and 'bar' commands.

            Arguments:
                args: A list of strings. This list is obtianed via
                    shlex.split().
            '''
            pass

def command(*commands, visible = True, internal = False, nargs = '*'):
    """Decorate a function to be the entry function of commands.

    Arguments:
        commands: Names of command that should trigger this function object.
        visible: This command is visible in tab completions.
        internal: The lexing rule is unchanged even if the parse_line() method
            is overloaded in the subclasses.
        nargs: Short for number of arguments. Similar to the nargs argument in
            the argparse module. Has the following valid values:
                    a non-negative integer
                    a list/set/tuple/range of non-negative integers
                    '*':        zero or more
                    '?':        zero or one
                    '+':        one or more
            The command method, whose interface is described below, will check
            the number of arguments. If it does not match this nargs argument,
            an error message will be printed to self.stderr and the shell is
            resumed.

    ----------------------------
    Interface of command methods:

        @command('foo', 'bar')
        def bar(self, args):
            '''The method invoked by 'foo' and 'bar' commands.

            Arguments:
                args: A list of strings. This list is obtianed via
                    shlex.split().
            '''
            pass
    """
    # Strict checking of the nargs argument.
    allowed_strs = {'*', '?', '+'}
    err_str = textwrap.dedent('''\
            command: '{}' is invalid, must be a non-negative integer, a
            list/set/tuple/range of non-negative integer, or one of {}
            '''.format(nargs, allowed_strs))
    if isinstance(nargs, str):
        if not nargs in allowed_strs:
            raise RuntimeError(err_str)
    elif isinstance(nargs, int):
        if nargs < 0:
            raise RuntimeError(err_str)
    else:
        nargs = list(nargs)
        for ele in nargs:
            if not isinstance(ele, int) or ele < 0:
                raise RuntimeError(err_str)

    def decorated_func(f):
        def inner_func(self, cmd, args):
            # Check the number of args according to nargs.
            n = len(args)
            if isinstance(nargs, str):
                if nargs == '*':
                    pass
                elif nargs == '?':
                    if n > 1:
                        self.error("{}: expect 0 or 1 argument, provided {}: {}\n".
                                format(cmd, n, args))
                        return
                elif nargs == '+':
                    if n == 0:
                        self.error("{}: expect 1 or more arguments, provided {}: {}\n".
                                format(cmd, n, args))
                        return
            elif isinstance(nargs, int):
                if n != nargs:
                        self.error("{}: expect {} arguments, provided {}: {}\n".
                                format(cmd, nargs, n, args))
                        return
            else:
                # nargs is already converted to a list.
                if not n in nargs:
                        self.error("{}: the number of arguments could be one of "
                                "{}, provided {}: {}\n".
                                format(cmd, nargs, n, args))
                        return
            return f(self, cmd, args)
        inner_func.__name__ = f.__name__
        inner_func.__doc__ = f.__doc__
        inner_func.__command__ = {
                'commands': list(commands),
                'visible': visible,
                'internal': internal,
        }
        # If f is deprecated, inner_func should also be deprecated. Do not use
        # the deprecated() function directly, as that adds duplicate warning
        # message.
        if isdeprecated(f):
            inner_func.__deprecated__ = True
        return inner_func
    return decorated_func