How to tell what kwargs are supported by a function

Is there any way to tell through code whether a function supports a particular kwarg or not? I have a list of args in a Dict, and I need to filter this Dict to those that are supported by the function being called but short of calling the function, and catching the Exception for unrecognized kwarg, I cannot figure out how to tell in advance if an arg is supported or not.

I don’t need to check arg types (though that would be useful), just their names.

Edit: Thanks everyone, this problem has now been solved. I use the following code that appears to work in versions of Julia >= 0.4:

function getKWArgs(f::Function, m::Union{Method,Void}=nothing)
    mts = methods(f)
    
    # In Julia 0.4, methods() returns a MethodTable
    # In Julia 0.6 it returns a MethodList that has an `mt` property with the MethodTable
    if isa(mts, MethodTable)
        table = mts
    elseif isdefined(mts, :mt)
        table = mts.mt
    else
        return Symbol[]
    end
    
    mts = collect(mts)
    
    # If there's no kwsorter in the MethodTable, this function does not accept kwargs
    if !isdefined(table, :kwsorter)
        return Symbol[]
    end
    
    method_index = 1
    if m == nothing
        m = mts[method_index]
    else
        method_index = findfirst(x -> m == x, mts)
    end
    
    kwsorter = collect(methods(table.kwsorter))[method_index]
    
    if isdefined(Base, :kwarg_decl)
        # Julia 0.6 has Base.kwarg_decl to get kwargs
        #   Code from https://github.com/JuliaDocs/DocStringExtensions.jl/blob/8e9da56adf4b6b5b6380d05c28f43e8b2d922f0c/src/utilities.jl#L243
        kwargs = Base.kwarg_decl(m, typeof(kwsorter))
        if isa(kwargs, Vector) && length(kwargs) > 0
            filter!(arg -> !occursin("#", string(arg)), kwargs)
            # Keywords *may* not be sorted correctly. We move the vararg one to the end.
            local index = findfirst(arg -> endswith(string(arg), "..."), kwargs)
            if index != nothing
                kwargs[index], kwargs[end] = kwargs[end], kwargs[index]
            end
            return kwargs
        end
    else
        # Julia 0.4 needs us to parse the kwsorter code as a string and pull out the kwargs
        # The only caveat is that we cannot tell if there is a vararg at the end. It's only
        # possible to tell if there are non kw varargs (ie, before the ;)
        kwargs = map(
                x -> Symbol(split(x, ",")[1][2:end]),  # String processing is faster than eval/parse
                filter(
                    x -> startswith(x, ":") && !endswith(x, ",0"),    # Non-kwargs have the third arg set to 0. kwargs have it set to 2 or 34 and could be other values
                    split(
                        replace(
                            string(kwsorter.func.code),               # Convert the kwsorter code to a string to parse it because we can't get anything out of :AST
                            r"^.*Any\[Any\[(Any\[.*?)(Any\[symbol\(\"##index|, *:\(begin).*"s,
                            s"\1"
                        ),
                        r"((\],)?Any\[|\]$)",
                        keep=false
                    )
                )
            )
        return kwargs
    end
    
    return Symbol[]
end
2 Likes

There is an example in DocStringExtensions.jl, but this is almost surely not something you want to keep doing at runtime.

It is very likely that you want to redesign your algorithm, eg keep a some table of what functions do what, and just call them conditional on that, or use some form of dispatch.

Another option is to make your function accept any kwargs and ignore them, e.g.

julia> f(; x = 1, kwargs...) = x

julia> f(hello = 5)
1

It isn’t my function to change though otherwise this would have been the simplest solution.

Thanks.

Our plan is to automate the building of this table by running a lookup the first time we see a function and on subsequent calls just using the cached value.

It’s not that we want to call functions conditionally. We receive a bunch of filters from the web UI. We have a “router” function that does things like parsing the JSON into a Dict and doing type checking and input validation/sanitisation on all the filters before passing them on to the routed function. The only thing left is to make sure we only send the supported subset of filters to the specified function and drop the others.

I do this type of things (1, 2, 3) in GMT.jl to allow aliases in kwargs. Maybe you could filter your allowed kwargs in a similar way.

Maybe of help: