def lift_args(cmd, args, kwargs):
    """
    Lift args lifts the arguments to the type annotations on cmd's parameters.
    """

    def lift_arg(typ, arg):
        # for stuff like int | None, allow either
        if get_origin(typ) in [types.UnionType, Union]:
            for t in get_args(typ):
                if t == types.NoneType:
                    # special case None? I don't know what this looks like
                    # coming over IPC
                    if arg == "":
                        return None
                    if arg is None:
                        return None
                    continue

                try:
                    return lift_arg(t, arg)
                except TypeError:
                    pass
            # uh oh, we couldn't lift it to anything
            raise TypeError(f"{arg} is not a {typ}")

        # for literals, check that it is one of the valid strings
        if get_origin(typ) is Literal:
            if arg not in get_args(typ):
                raise TypeError(f"{arg} is not one of {get_origin(typ)}")
            return arg

        if typ is bool:
            # >>> bool("False") is True
            # True
            # ... but we want it to be false :)
            if arg == "True" or arg is True:
                return True
            if arg == "False" or arg is False:
                return False
            raise TypeError(f"{arg} is not a bool")

        if typ is Any:
            # can't do any lifting if we don't know the type
            return arg

        if typ in [_Extension, Layout]:
            # these are "complex" objects that can't be created with a
            # single string argument. we generally don't expect people to
            # be passing these over the command line, so let's ignore then.
            return arg

        if get_origin(typ) in [list, dict]:
            # again, we do not want to be in the business of parsing
            # lists/dicts of types out of strings; just pass on whatever we
            # got
            return arg

        return typ(arg)

    converted_args = []
    converted_kwargs = dict()

    params = typing.get_type_hints(cmd, globalns=globals())

    non_return_annotated_args = filter(lambda k: k != "return", params.keys())
    for param, arg in zip(non_return_annotated_args, args):
        converted_args.append(lift_arg(params[param], arg))

    # if not all args were annotated, we need to keep them anyway. note
    # that mixing some annotated and not annotated args will not work well:
    # we will reorder args here and cause problems. this is solveable but
    # somewhat ugly, and we can avoid it by always annotating all
    # parameters.
    #
    # if we really want to fix this, we
    # inspect.signature(foo).parameters.keys() gives us the ordered
    # parameters to reason about.
    if len(converted_args) < len(args):
        converted_args.extend(args[len(converted_args) :])

    for k, v in kwargs.items():
        # if this kwarg has a type annotation, use it
        if k in params:
            converted_kwargs[k] = lift_arg(params[k], v)
        else:
            converted_kwargs[k] = v

    return tuple(converted_args), converted_kwargs

    Lift args lifts the arguments to the type annotations on cmd's parameters.
    
libqtile/command/interface.py