AnswerDotAI / AnswerDotAI/fastcore

Support call_parse+docments+delegates compatability

Open
#398 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Jupyter Notebook
Stars
1.1k
Forks
295
Avg merge
1d 6h
Merged PRs (30d)
7

Description

My solution here is to modify `delegates` to output kwargs with `Param` type hints, thus propagating the docments through to call_parse. This solution is a tad ugly since `cast_to_call_parse=True` needs to be passed so that the delegates knows that it
is supposed to change everything to Param's.

## Problem
call_parse does not work if using a delegates+docments combo. For example:
```python
class A(object):
"something"
def __init__(self,
a:int=5 # This is a parameter
):
pass

@call_parse
@delegates(A)
def some_callable(
b:int=8, # A paramter that is defined in the call method.
**kwargs
):
"Some doc"
pass

show_doc(A)
show_doc(some_callable)
print(anno_parser(some_callable).format_help())
```
Outputs:
```bash
TypeError Traceback (most recent call last)
Input In [19], in
17 show_doc(A)
18 show_doc(some_callable)
---> 19 anno_parser(some_callable)

File ~/.local/lib/python3.9/site-packages/fastcore/script.py:75, in anno_parser(func, prog, from_name)
73 param = v.anno
74 if not isinstance(param,Param): param = Param(v.docment, v.anno)
---> 75 param.set_default(v.default)
76 p.add_argument(f"{param.pre}{k}", **param.kwargs)
77 p.add_argument(f"--pdb", help=argparse.SUPPRESS, action='store_true')

File ~/.local/lib/python3.9/site-packages/fastcore/script.py:48, in Param.set_default(self, d)
46 if d==inspect.Parameter.empty: self.opt = False
47 else: self.default = d
---> 48 if self.default is not None: self.help += f" (default: {self.default})"

TypeError: unsupported operand type(s) for +=: 'NoneType' and 'str'
```

## Slightly Dirty Solution
*also buggy, it doesnt handle default values very well
```python
# export
import inspect
from inspect import Parameter
from fastcore.docments import docments
def delegates(to=None, keep=False, but=None, cast_to_call_parse=False):
"Decorator: replace `**kwargs` in signature with params from `to`"
if but is None: but = []

def _f(f):
if to is None: to_f,from_f = f.__base__.__init__,f.__init__
else: to_f,from_f = to.__init__ if isinstance(to,type) else to,f
from_f = getattr(from_f,'__func__',from_f)
to_f = getattr(to_f,'__func__',to_f)
if hasattr(from_f,'__delwrap__'): return f
sig = inspect.signature(from_f)
sigd = dict(sig.parameters)
k = sigd.pop('kwargs')
s2 = {k:v for k,v in inspect.signature(to_f).parameters.items()
if v.default != inspect.Parameter.empty and k not in sigd and k not in but}

if cast_to_call_parse:
for k,v in docments(to_f, full=True, returns=False).items():
param = v.anno
if v.default == inspect.Parameter.empty or k in sigd or k in but: continue
if not isinstance(param,Param): param = Param(v.docment, v.anno)
# if v.default is None
param.set_default(ifnone(v.default,'None'))
s2[k]=Parameter(k,Parameter.KEYWORD_ONLY,annotation=param)

sigd.update(s2)
if keep: sigd['kwargs'] = k
else: from_f.__delwrap__ = to_f
from_f.__signature__ = sig.replace(parameters=sigd.values())
return f
return _f
```
then if we set the flag:
```python
@call_parse
@delegates(A,cast_to_call_parse=True)
def some_callable(
b:int=8, # A paramter that is defined in the call method.
**kwargs
):
"Some doc"
pass

show_doc(A)
show_doc(some_callable)
print(anno_parser(some_callable).format_help())
```

Outputs:
```bash
optional arguments:
-h, --help show this help message and exit
--b B A paramter that is defined in the call method. (default: 8)
--a A This is a parameter (default: 5) (default: 5)
```

Contributor guide

Open the contributing guide

Research direction

Start with the traceback in fastcore/script.py, especially anno_parser and Param.set_default, then inspect the delegates and docments interaction shown in the reproduction. Verify the call_parse and delegates combination handles documented defaults and produces help output without the reported TypeError; no specific test file is named in the issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.