fastcore icon indicating copy to clipboard operation
fastcore copied to clipboard

`@call_parse`: Keyword only arguments help-msg not showing in help

Open mennowitteveen opened this issue 1 year ago • 0 comments

This is one of my first Issues, so it probably isn't perfect. Sorry if I missed other (related) Issues.

The problem:

The issues is that using the "*" star syntax to define keyword-only arguments in functions, makes the help message of the arguments after the the star (keyword-only arguments) not show up:

@call_parse
def fun(
        a:int=3,       # The a parameter
        b:str='string', # comment parameter b
        *, # Keywords only from now on
        c:float=0.42,  # c param with comment  <-- not shown in help
        d='var',         # The d param comment <-- not shown in help
        e=42           # Comment 4 e           <-- not shown in help
    ):
    "->Documentation<-"
    return True

print(anno_parser(fun).format_help()) 

The output:

usage: ipykernel_launcher.py [-h] [--a A] [--b B] [--c C] [--d D] [--e E]

->Documentation<-

options:
  -h, --help  show this help message and exit
  --a A       The a parameter (default: 3)
  --b B       String parameter b (default: string)
  --c C       (default: 0.42)
  --d D       (default: var)
  --e E       (default: 42)

The behavior is also present if I save to a file and run it from the commandline.

The help message for arg c,d & e are missing. If I comment out the "*, " line it works again.

The cause/potential solution:

I think I have tracked down why this happens:

#from fastcore.docments import _param_locs
def _param_locs(s, returns=True):
    "`dict` of parameter line numbers to names"
    body = _parses(s).body
    if len(body)==1: #or not isinstance(body[0], FunctionDef): return None
        defn = body[0]
        if isinstance(defn, FunctionDef):
            #res = {arg.lineno:arg.arg for arg in defn.args.args} # Original line
            # Suggested change that makes it work: <---------------------------
            res = {arg.lineno:arg.arg for arg in (defn.args.args + defn.args.kwonlyargs)} 
            if returns and defn.returns: res[defn.returns.lineno] = 'return'
            return res
        elif isdataclass(s):
            res = {arg.lineno:arg.target.id for arg in defn.body if isinstance(arg, AnnAssign)}
            return res
    return None

Since _param_locs does not return keyword-only arguments (defn.args.kwonlyargs) it does not get matched with its comments and hence does not lead to a help message.

Its probably a bit preliminary, but perhaps I can turn this into a PR after some testing?

mennowitteveen avatar Jul 27 '23 09:07 mennowitteveen