Skip to content

Commit cf29ed8

Browse files
authored
Merge pull request #169 from rsomani95/param-repr
add __repr__ to Param for augmented function documentation
2 parents 2a12d01 + 45c4528 commit cf29ed8

File tree

3 files changed

+89
-5
lines changed

3 files changed

+89
-5
lines changed

fastcore/_nbdev.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@
209209
"store_true": "08_script.ipynb",
210210
"store_false": "08_script.ipynb",
211211
"bool_arg": "08_script.ipynb",
212+
"clean_type_str": "08_script.ipynb",
212213
"Param": "08_script.ipynb",
213214
"anno_parser": "08_script.ipynb",
214215
"args_from_prog": "08_script.ipynb",

fastcore/script.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/08_script.ipynb (unless otherwise specified).
22

3-
__all__ = ['store_true', 'store_false', 'bool_arg', 'Param', 'anno_parser', 'args_from_prog', 'SCRIPT_INFO',
4-
'call_parse']
3+
__all__ = ['store_true', 'store_false', 'bool_arg', 'clean_type_str', 'Param', 'anno_parser', 'args_from_prog',
4+
'SCRIPT_INFO', 'call_parse']
55

66
# Cell
77
import inspect,functools
@@ -24,6 +24,13 @@ def bool_arg(v):
2424
"Use as `type` for `Param` to get `bool` behavior"
2525
return str2bool(v)
2626

27+
# Cell
28+
def clean_type_str(x:str):
29+
x = str(x)
30+
x = re.sub("(class|function|__main__\.|\ at.*)", '', x)
31+
x = re.sub("(<|>|'|\ )", '', x) # spl characters
32+
return x
33+
2734
# Cell
2835
class Param:
2936
"A parameter in a function used in `anno_parser` or `call_parse`"
@@ -44,6 +51,11 @@ def pre(self): return '--' if self.opt else ''
4451
@property
4552
def kwargs(self): return {k:v for k,v in self.__dict__.items()
4653
if v is not None and k!='opt' and k[0]!='_'}
54+
def __repr__(self):
55+
if self.help is None and self.type is None: return ""
56+
if self.help is None and self.type is not None: return f"{clean_type_str(self.type)}"
57+
if self.help is not None and self.type is None: return f"<{self.help}>"
58+
if self.help is not None and self.type is not None: return f"{clean_type_str(self.type)} <{self.help}>"
4759

4860
# Cell
4961
def anno_parser(func, prog=None, from_name=False):
@@ -93,4 +105,4 @@ def _f(*args, **kwargs):
93105
setattr(mod, func.__name__, _f)
94106
SCRIPT_INFO.func = func.__name__
95107
return _f()
96-
else: return _f
108+
else: return _f

nbs/08_script.ipynb

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,35 @@
189189
" return str2bool(v)"
190190
]
191191
},
192+
{
193+
"cell_type": "code",
194+
"execution_count": null,
195+
"metadata": {},
196+
"outputs": [],
197+
"source": [
198+
"#export\n",
199+
"def clean_type_str(x:str):\n",
200+
" x = str(x)\n",
201+
" x = re.sub(\"(class|function|__main__\\.|\\ at.*)\", '', x)\n",
202+
" x = re.sub(\"(<|>|'|\\ )\", '', x) # spl characters\n",
203+
" return x"
204+
]
205+
},
206+
{
207+
"cell_type": "code",
208+
"execution_count": null,
209+
"metadata": {},
210+
"outputs": [],
211+
"source": [
212+
"class Test: pass\n",
213+
"\n",
214+
"test_eq(clean_type_str(argparse.ArgumentParser), 'argparse.ArgumentParser')\n",
215+
"test_eq(clean_type_str(Test), 'Test')\n",
216+
"test_eq(clean_type_str(int), 'int')\n",
217+
"test_eq(clean_type_str(float), 'float')\n",
218+
"test_eq(clean_type_str(store_false), 'store_false')"
219+
]
220+
},
192221
{
193222
"cell_type": "code",
194223
"execution_count": null,
@@ -214,7 +243,24 @@
214243
" def pre(self): return '--' if self.opt else ''\n",
215244
" @property\n",
216245
" def kwargs(self): return {k:v for k,v in self.__dict__.items()\n",
217-
" if v is not None and k!='opt' and k[0]!='_'}"
246+
" if v is not None and k!='opt' and k[0]!='_'}\n",
247+
" def __repr__(self):\n",
248+
" if self.help is None and self.type is None: return \"\"\n",
249+
" if self.help is None and self.type is not None: return f\"{clean_type_str(self.type)}\"\n",
250+
" if self.help is not None and self.type is None: return f\"<{self.help}>\"\n",
251+
" if self.help is not None and self.type is not None: return f\"{clean_type_str(self.type)} <{self.help}>\""
252+
]
253+
},
254+
{
255+
"cell_type": "code",
256+
"execution_count": null,
257+
"metadata": {},
258+
"outputs": [],
259+
"source": [
260+
"test_eq(repr(Param(\"Help goes here\")), '<Help goes here>')\n",
261+
"test_eq(repr(Param(\"Help\", int)), 'int <Help>')\n",
262+
"test_eq(repr(Param(help=None, type=int)), 'int')\n",
263+
"test_eq(repr(Param(help=None, type=None)), '')"
218264
]
219265
},
220266
{
@@ -223,7 +269,31 @@
223269
"source": [
224270
"Each parameter in your function should have an annotation `Param(...)`. You can pass the following when calling `Param`: `help`,`type`,`opt`,`action`,`nargs`,`const`,`choices`,`required` (i.e. it takes the same parameters as `argparse.ArgumentParser.add_argument`, plus `opt`). Except for `opt`, all of these are just passed directly to `argparse`, so you have all the power of that module at your disposal. Generally you'll want to pass at least `help` (since this is provided as the help string for that parameter) and `type` (to ensure that you get the type of data you expect).\n",
225271
"\n",
226-
"`opt` is a bool that defines whether a param is optional or required (positional) - but you'll generally not need to set this manually, because fastcore.script will set it for you automatically based on *default* values. You should provide a default (after the `=`) for any *optional* parameters. If you don't provide a default for a parameter, then it will be a *positional* parameter."
272+
"`opt` is a bool that defines whether a param is optional or required (positional) - but you'll generally not need to set this manually, because fastcore.script will set it for you automatically based on *default* values. You should provide a default (after the `=`) for any *optional* parameters. If you don't provide a default for a parameter, then it will be a *positional* parameter.\n",
273+
"\n",
274+
"Param's `__repr__` also allows for more informative function annotation when looking up the function's doc using shift+tab. You see the type annotation (if there is one) and the accompanying help documentation with it."
275+
]
276+
},
277+
{
278+
"cell_type": "code",
279+
"execution_count": null,
280+
"metadata": {},
281+
"outputs": [],
282+
"source": [
283+
"def f(required:Param(\"Required param\", int),\n",
284+
" a:Param(\"param 1\", bool_arg),\n",
285+
" b:Param(\"param 2\", str)=\"test\"):\n",
286+
" \"my docs\"\n",
287+
" ..."
288+
]
289+
},
290+
{
291+
"cell_type": "code",
292+
"execution_count": null,
293+
"metadata": {},
294+
"outputs": [],
295+
"source": [
296+
"f?"
227297
]
228298
},
229299
{
@@ -431,6 +501,7 @@
431501
"Converted 03_xtras.ipynb.\n",
432502
"Converted 04_dispatch.ipynb.\n",
433503
"Converted 05_transform.ipynb.\n",
504+
"Converted 06_logargs.ipynb.\n",
434505
"Converted 07_meta.ipynb.\n",
435506
"Converted 08_script.ipynb.\n",
436507
"Converted index.ipynb.\n"

0 commit comments

Comments
 (0)