Skip to content

Commit 9937237

Browse files
committed
fixes #687
1 parent e628b4b commit 9937237

3 files changed

Lines changed: 95 additions & 9 deletions

File tree

fastcore/_modidx.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,8 @@
253253
'fastcore.docments.get_source': ('docments.html#get_source', 'fastcore/docments.py'),
254254
'fastcore.docments.isdataclass': ('docments.html#isdataclass', 'fastcore/docments.py'),
255255
'fastcore.docments.parse_docstring': ('docments.html#parse_docstring', 'fastcore/docments.py'),
256-
'fastcore.docments.qual_name': ('docments.html#qual_name', 'fastcore/docments.py')},
256+
'fastcore.docments.qual_name': ('docments.html#qual_name', 'fastcore/docments.py'),
257+
'fastcore.docments.sig2str': ('docments.html#sig2str', 'fastcore/docments.py')},
257258
'fastcore.docscrape': {},
258259
'fastcore.foundation': { 'fastcore.foundation.CollBase': ('foundation.html#collbase', 'fastcore/foundation.py'),
259260
'fastcore.foundation.CollBase.__delitem__': ( 'foundation.html#collbase.__delitem__',

fastcore/docments.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# %% ../nbs/04_docments.ipynb 2
66
from __future__ import annotations
77

8-
import re,ast
8+
import re,ast,inspect
99
from tokenize import tokenize,COMMENT
1010
from ast import parse,FunctionDef,AsyncFunctionDef,AnnAssign
1111
from io import BytesIO
@@ -20,7 +20,7 @@
2020

2121
# %% auto 0
2222
__all__ = ['empty', 'docstring', 'parse_docstring', 'isdataclass', 'get_dataclass_source', 'get_source', 'get_name', 'qual_name',
23-
'docments', 'extract_docstrings']
23+
'docments', 'sig2str', 'extract_docstrings']
2424

2525
# %% ../nbs/04_docments.ipynb
2626
def docstring(sym):
@@ -180,13 +180,37 @@ def _update_docments(f, r):
180180
if not full: r = {k:v['docment'] for k,v in r.items()}
181181
return AttrDict(r)
182182

183+
# %% ../nbs/04_docments.ipynb
184+
def sig2str(func):
185+
"Generate function signature with docments as comments"
186+
docs = docments(func, full=True)
187+
params = []
188+
for k,v in docs.items():
189+
if k == 'return': continue
190+
anno = getattr(v['anno'], '__name__', str(v['anno'])) if v['anno'] != inspect._empty else ''
191+
if '|' in str(v['anno']): anno = str(v['anno'])
192+
p = k + (f':{anno}' if anno and anno != 'inspect._empty' else '')
193+
if v['default'] != inspect._empty:
194+
d = getattr(v['default'], '__name__', v['default']) if hasattr(v['default'], '__name__') else v['default']
195+
p += f'={d}' if d is not None else '=None'
196+
if v['docment']: p += f' # {v["docment"]}'
197+
params.append(p)
198+
199+
ret = docs.get('return', {})
200+
ret_str = ':'
201+
if ret and ret.get('anno')!=inspect._empty:
202+
ret_str = f"->{getattr(ret['anno'], '__name__', str(ret['anno']))}" + (f': # {ret["docment"]}' if ret.get('docment') else ':')
203+
doc_str = f' "{func.__doc__}"' if func.__doc__ else ''
204+
return f"def {func.__name__}(\n " + ",\n ".join(params) + f"\n){ret_str}\n{doc_str}"
205+
183206
# %% ../nbs/04_docments.ipynb
184207
def _get_params(node):
185208
params = [a.arg for a in node.args.args]
186209
if node.args.vararg: params.append(f"*{node.args.vararg.arg}")
187210
if node.args.kwarg: params.append(f"**{node.args.kwarg.arg}")
188211
return ", ".join(params)
189212

213+
# %% ../nbs/04_docments.ipynb
190214
class _DocstringExtractor(ast.NodeVisitor):
191215
def __init__(self): self.docstrings,self.cls,self.cls_init = {},None,None
192216

nbs/04_docments.ipynb

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"#|export\n",
2828
"from __future__ import annotations\n",
2929
"\n",
30-
"import re,ast\n",
30+
"import re,ast,inspect\n",
3131
"from tokenize import tokenize,COMMENT\n",
3232
"from ast import parse,FunctionDef,AsyncFunctionDef,AnnAssign\n",
3333
"from io import BytesIO\n",
@@ -840,7 +840,7 @@
840840
"output_type": "stream",
841841
"text": [
842842
"The sum of two numbers.\n",
843-
" \n",
843+
"\n",
844844
" Used to demonstrate numpy-style docstrings.\n",
845845
"\n",
846846
"Parameters\n",
@@ -1019,7 +1019,8 @@
10191019
"\n",
10201020
"@delegates(_a)\n",
10211021
"def _b(b:str, # Second\n",
1022-
" **kwargs): \n",
1022+
" **kwargs\n",
1023+
" ): # Return nothing\n",
10231024
" return b, (_a(**kwargs)) \n",
10241025
"\n",
10251026
"docments(_b)"
@@ -1073,7 +1074,7 @@
10731074
"@delegates(_c)\n",
10741075
"def _d(c:int, # First\n",
10751076
" b:str, **kwargs\n",
1076-
" )->int:\n",
1077+
" )->int: # Return an int\n",
10771078
" return c, _c(b, **kwargs)"
10781079
]
10791080
},
@@ -1090,6 +1091,58 @@
10901091
"test_eq(docments(_d, full=True).keys() & _argset, _argset) # _d has the args a,b,c and return"
10911092
]
10921093
},
1094+
{
1095+
"cell_type": "code",
1096+
"execution_count": null,
1097+
"metadata": {},
1098+
"outputs": [],
1099+
"source": [
1100+
"#| export\n",
1101+
"def sig2str(func):\n",
1102+
" \"Generate function signature with docments as comments\"\n",
1103+
" docs = docments(func, full=True)\n",
1104+
" params = []\n",
1105+
" for k,v in docs.items():\n",
1106+
" if k == 'return': continue\n",
1107+
" anno = getattr(v['anno'], '__name__', str(v['anno'])) if v['anno'] != inspect._empty else ''\n",
1108+
" if '|' in str(v['anno']): anno = str(v['anno'])\n",
1109+
" p = k + (f':{anno}' if anno and anno != 'inspect._empty' else '')\n",
1110+
" if v['default'] != inspect._empty:\n",
1111+
" d = getattr(v['default'], '__name__', v['default']) if hasattr(v['default'], '__name__') else v['default']\n",
1112+
" p += f'={d}' if d is not None else '=None'\n",
1113+
" if v['docment']: p += f' # {v[\"docment\"]}'\n",
1114+
" params.append(p)\n",
1115+
" \n",
1116+
" ret = docs.get('return', {})\n",
1117+
" ret_str = ':'\n",
1118+
" if ret and ret.get('anno')!=inspect._empty:\n",
1119+
" ret_str = f\"->{getattr(ret['anno'], '__name__', str(ret['anno']))}\" + (f': # {ret[\"docment\"]}' if ret.get('docment') else ':')\n",
1120+
" doc_str = f' \"{func.__doc__}\"' if func.__doc__ else ''\n",
1121+
" return f\"def {func.__name__}(\\n \" + \",\\n \".join(params) + f\"\\n){ret_str}\\n{doc_str}\""
1122+
]
1123+
},
1124+
{
1125+
"cell_type": "code",
1126+
"execution_count": null,
1127+
"metadata": {},
1128+
"outputs": [
1129+
{
1130+
"name": "stdout",
1131+
"output_type": "stream",
1132+
"text": [
1133+
"def _d(\n",
1134+
" b:str # Second,\n",
1135+
" a:int=2 # Third,\n",
1136+
" c:int # First\n",
1137+
")->int: # Return an int\n",
1138+
"\n"
1139+
]
1140+
}
1141+
],
1142+
"source": [
1143+
"print(sig2str(_d))"
1144+
]
1145+
},
10931146
{
10941147
"cell_type": "markdown",
10951148
"metadata": {},
@@ -1108,8 +1161,16 @@
11081161
" params = [a.arg for a in node.args.args]\n",
11091162
" if node.args.vararg: params.append(f\"*{node.args.vararg.arg}\")\n",
11101163
" if node.args.kwarg: params.append(f\"**{node.args.kwarg.arg}\")\n",
1111-
" return \", \".join(params)\n",
1112-
"\n",
1164+
" return \", \".join(params)"
1165+
]
1166+
},
1167+
{
1168+
"cell_type": "code",
1169+
"execution_count": null,
1170+
"metadata": {},
1171+
"outputs": [],
1172+
"source": [
1173+
"#| export\n",
11131174
"class _DocstringExtractor(ast.NodeVisitor):\n",
11141175
" def __init__(self): self.docstrings,self.cls,self.cls_init = {},None,None\n",
11151176
"\n",

0 commit comments

Comments
 (0)