def __multi_arity_dispatch_fn(  # pylint: disable=too-many-arguments,too-many-locals
    ctx: GeneratorContext,
    name: str,
    arity_map: Mapping[int, str],
    default_name: Optional[str] = None,
    max_fixed_arity: Optional[int] = None,
    meta_node: Optional[MetaNode] = None,
    is_async: bool = False,
) -> GeneratedPyAST:
    """Return the Python AST nodes for a argument-length dispatch function
    for multi-arity functions.

    def fn(*args):
        nargs = len(args)
        method = __fn_dispatch_map.get(nargs)
        if method:
            return method(*args)
        # Only if default
        if nargs > max_fixed_arity:
            return default(*args)
        raise RuntimeError
    """
    dispatch_map_name = f"{name}_dispatch_map"

    dispatch_keys, dispatch_vals = [], []
    for k, v in arity_map.items():
        dispatch_keys.append(ast.Num(k))
        dispatch_vals.append(ast.Name(id=v, ctx=ast.Load()))

    # Async functions should return await, otherwise just return
    handle_return = __handle_async_return if is_async else __handle_return

    nargs_name = genname("nargs")
    method_name = genname("method")
    body = [
        ast.Assign(
            targets=[ast.Name(id=nargs_name, ctx=ast.Store())],
            value=ast.Call(
                func=ast.Name(id="len", ctx=ast.Load()),
                args=[ast.Name(id=_MULTI_ARITY_ARG_NAME, ctx=ast.Load())],
                keywords=[],
            ),
        ),
        ast.Assign(
            targets=[ast.Name(id=method_name, ctx=ast.Store())],
            value=ast.Call(
                func=ast.Attribute(
                    value=ast.Name(id=dispatch_map_name, ctx=ast.Load()),
                    attr="get",
                    ctx=ast.Load(),
                ),
                args=[ast.Name(id=nargs_name, ctx=ast.Load())],
                keywords=[],
            ),
        ),
        ast.If(
            test=ast.Compare(
                left=ast.NameConstant(None),
                ops=[ast.IsNot()],
                comparators=[ast.Name(id=method_name, ctx=ast.Load())],
            ),
            body=[
                handle_return(
                    ast.Call(
                        func=ast.Name(id=method_name, ctx=ast.Load()),
                        args=[
                            ast.Starred(
                                value=ast.Name(
                                    id=_MULTI_ARITY_ARG_NAME, ctx=ast.Load()
                                ),
                                ctx=ast.Load(),
                            )
                        ],
                        keywords=[],
                    )
                )
            ],
            orelse=[]
            if default_name is None
            else [
                ast.If(
                    test=ast.Compare(
                        left=ast.Name(id=nargs_name, ctx=ast.Load()),
                        ops=[ast.GtE()],
                        comparators=[ast.Num(max_fixed_arity)],
                    ),
                    body=[
                        handle_return(
                            ast.Call(
                                func=ast.Name(id=default_name, ctx=ast.Load()),
                                args=[
                                    ast.Starred(
                                        value=ast.Name(
                                            id=_MULTI_ARITY_ARG_NAME, ctx=ast.Load()
                                        ),
                                        ctx=ast.Load(),
                                    )
                                ],
                                keywords=[],
                            )
                        )
                    ],
                    orelse=[],
                )
            ],
        ),
        ast.Raise(
            exc=ast.Call(
                func=_load_attr("basilisp.lang.runtime.RuntimeException"),
                args=[
                    ast.Str(f"Wrong number of args passed to function: {name}"),
                    ast.Name(id=nargs_name, ctx=ast.Load()),
                ],
                keywords=[],
            ),
            cause=None,
        ),
    ]

    py_fn_node = ast.AsyncFunctionDef if is_async else ast.FunctionDef
    meta_deps, meta_decorators = __fn_meta(ctx, meta_node)
    return GeneratedPyAST(
        node=ast.Name(id=name, ctx=ast.Load()),
        dependencies=chain(
            [
                ast.Assign(
                    targets=[ast.Name(id=dispatch_map_name, ctx=ast.Store())],
                    value=ast.Dict(keys=dispatch_keys, values=dispatch_vals),
                )
            ],
            meta_deps,
            [
                py_fn_node(
                    name=name,
                    args=ast.arguments(
                        args=[],
                        kwarg=None,
                        vararg=ast.arg(arg=_MULTI_ARITY_ARG_NAME, annotation=None),
                        kwonlyargs=[],
                        defaults=[],
                        kw_defaults=[],
                    ),
                    body=body,
                    decorator_list=list(chain(meta_decorators, [_BASILISP_FN_FN_NAME])),
                    returns=None,
                )
            ],
        ),
    )
Return the Python AST nodes for a argument-length dispatch function
    for multi-arity functions.

    def fn(*args):
        nargs = len(args)
        method = __fn_dispatch_map.get(nargs)
        if method:
            return method(*args)
        # Only if default
        if nargs > max_fixed_arity:
            return default(*args)
        raise RuntimeError