Check if function accepts specific keyword argument

Suppose I have

julia> f(;y=1) = y
f (generic function with 1 method)

If I want to check whether f accepts y as a keyword argument I can do

julia> hasmethod(f, Tuple{}, (:y,))
true

But this requires knowing the number of non-keyword arguments.
For instance

julia> g(x1, x2; y=1) = (x1 + x2) * y
g (generic function with 1 method)

julia> hasmethod(g, Tuple{Any, Any}, (:y,))
true

julia> hasmethod(g, Tuple{Any}, (:y,))
false

julia> hasmethod(g, Tuple{}, (:y,))
false

How can I check whether g accepts y as a keyword, without knowing how many non-keyword arguments it accepts?

Thanks in advance!!

1 Like

This is the best I’ve got so far,

function check_if_keyword_accepted(func, keyword, max_args=15)
    keyword = Symbol(keyword)
    for i in 0:max_args
        if hasmethod(func, NTuple{i,Any}, (keyword,))
            return true
        end
    end
    return false
end

Which works for my use case, if we know the maximum number of non-keyword arguments.

Is there anything more Julia-like?

Cheers

I think this question is not well posed. You need to know how many positional parameters and of which type you want/need to use before you can ask whether you can also add some keyword. This is because each method (identified by the types of the positional arguments) can have different keyword arguments. Consider this example:

julia> foo(x::Int, y::Int; z=3)=x+y*z
foo (generic function with 1 method)

julia> foo(a::String, b::String; c=2) = a*b^c
foo (generic function with 2 methods)
  1. There is no method that corresponding to foo(Any, Any) so your check_if_keyword_accepted would always return false.
  2. If you somehow checked whether some method could take a keyword c then just getting true would be rather useless without knowing more of the signature.

What you could probably do is check the output of methods(foo)

julia> methods(foo)
# 2 methods for generic function "foo" from Main:
 [1] foo(a::String, b::String; c)
     @ REPL[2]:1
 [2] foo(x::Int64, y::Int64; z)
     @ REPL[1]:1

I inspected the code for printing this using @edit show(stdout, methods(foo)[1]) and found the function Base.kwarg_decl that lists the possible keywords as symbols. So you could do:

julia> function check_if_keyword_accepted(func, keyword)
    keyword = Symbol(keyword)
    for m in methods(func)
        if keyword in Base.kwarg_decl(m)
            show(m) # or return m, or collect the compatible methods, ....
            #return true
        end
    end
    # return false
end

julia> check_if_keyword_accepted(foo, :c)
foo(a::String, b::String; c) @ Main REPL[3]:1
julia> check_if_keyword_accepted(foo, :z)
foo(x::Int64, y::Int64; z) @ Main REPL[1]:1
2 Likes

This would be my 2nd resort, too. Documentation would be the 1st. This works well because methods generally don’t vary keyword sets as much as your example. Doing so makes a function call like foo(L, M; c = N) not very generic, which warrants separating into different functions. I either see a documented keyword set apply to all methods of a function, or I see keywords be added (not removed) to 1 (not more) subset of the methods with an obvious condition, like dims when an argument is an AbstractArray.

1 Like