AnswerDotAI / AnswerDotAI/fastcore
Support call_parse+docments+delegates compatability
- 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
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