Skip to content

Commit db3813d

Browse files
committed
Made tapify work for classes and dataclasses and help strings and added tests
1 parent 9f53818 commit db3813d

File tree

3 files changed

+447
-104
lines changed

3 files changed

+447
-104
lines changed

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
package_data={'tap': ['py.typed']},
3030
install_requires=[
3131
'typing_extensions >= 3.7.4',
32-
'typing-inspect >= 0.7.1'
32+
'typing-inspect >= 0.7.1',
33+
'docstring-parser >= 0.15'
3334
],
3435
tests_require=['pytest'],
3536
classifiers=[

tap/tapify.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,58 @@
1-
"""Tapify module, which can run a function by parsing arguments for the function from the command line."""
1+
"""Tapify module, which can initialize a class or run a function by parsing arguments from the command line."""
22
from inspect import signature, Parameter
3-
from typing import Any, Callable, List, Optional
3+
from typing import Any, Callable, List, Optional, TypeVar, Union
4+
5+
from docstring_parser import parse
46

57
from tap import Tap
68

9+
InputType = TypeVar('InputType')
10+
OutputType = TypeVar('OutputType')
11+
712

8-
def tapify(function: Callable,
13+
def tapify(class_or_function: Union[Callable[[InputType], OutputType], OutputType],
914
args: Optional[List[str]] = None,
1015
known_only: bool = False,
11-
**func_kwargs) -> Any:
12-
"""Runs a function by parsing arguments for the function from the command line.
16+
**func_kwargs) -> OutputType:
17+
"""Tapify initializes a class or runs a function by parsing arguments from the command line.
1318
14-
:param function: The function to run with the provided arguments.
19+
:param class_or_function: The class or function to run with the provided arguments.
1520
:param args: Arguments to parse. If None, arguments are parsed from the command line.
1621
:param known_only: If true, ignores extra arguments and only parses known arguments.
1722
:param func_kwargs: Additional keyword arguments for the function. These act as default values when
1823
parsing the command line arguments and overwrite the function defaults but
1924
are overwritten by the parsed command line arguments.
2025
"""
21-
# Get signature from function
22-
sig = signature(function)
26+
# Get signature from class or function
27+
sig = signature(class_or_function)
28+
29+
# Parse class or function docstring in one line
30+
if isinstance(class_or_function, type) and class_or_function.__init__.__doc__ is not None:
31+
doc = class_or_function.__init__.__doc__
32+
else:
33+
doc = class_or_function.__doc__
34+
35+
# Parse docstring
36+
docstring = parse(doc)
2337

24-
# Create a Tap object with the arguments of the function
25-
tap = Tap(description=function.__doc__)
38+
# Get the description of each argument in the class init or function
39+
param_to_description = {param.arg_name: param.description for param in docstring.params}
40+
41+
# Create a Tap object
42+
tap = Tap(description=f'{docstring.short_description}\n{docstring.long_description}')
43+
44+
# Add arguments of class init or function to the Tap object
2645
for param_name, param in sig.parameters.items():
2746
tap_kwargs = {}
2847

2948
# Get type of the argument
3049
if param.annotation != Parameter.empty:
31-
tap._annotations[param.name] = param.annotation
50+
# Any type defaults to str (needed for dataclasses where all non-default attributes must have a type)
51+
if param.annotation is Any:
52+
tap._annotations[param.name] = str
53+
# Otherwise, get the type of the argument
54+
else:
55+
tap._annotations[param.name] = param.annotation
3256

3357
# Get the default or required of the argument
3458
if param.name in func_kwargs:
@@ -39,6 +63,10 @@ def tapify(function: Callable,
3963
else:
4064
tap_kwargs['required'] = True
4165

66+
# Get the help string of the argument
67+
if param.name in param_to_description:
68+
tap.class_variables[param.name] = {'comment': param_to_description[param.name]}
69+
4270
# Add the argument to the Tap object
4371
tap._add_argument(f'--{param_name}', **tap_kwargs)
4472

@@ -52,5 +80,5 @@ def tapify(function: Callable,
5280
known_only=known_only
5381
)
5482

55-
# Run the function with the parsed arguments
56-
return function(**args.as_dict())
83+
# Initialize the class or run the function with the parsed arguments
84+
return class_or_function(**args.as_dict())

0 commit comments

Comments
 (0)