Skip to content

Commit 8b974d7

Browse files
authored
Merge pull request #133 from kddubey/support-positional-only-arguments
`tapify` support positional-only arguments
2 parents 821c08c + ddd177d commit 8b974d7

File tree

3 files changed

+455
-91
lines changed

3 files changed

+455
-91
lines changed

tap/tapify.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ class _ArgData:
5353
"Whether or not the argument must be passed in"
5454

5555
default: Any
56-
"Value of the argument if the argument isn't passed in. This gets ignored if is_required"
56+
"Value of the argument if the argument isn't passed in. This gets ignored if `is_required`"
5757

5858
description: Optional[str] = ""
5959
"Human-readable description of the argument"
6060

61+
is_positional_only: bool = False
62+
"Whether or not the argument must be provided positionally"
63+
6164

6265
@dataclasses.dataclass(frozen=True)
6366
class _TapData:
@@ -218,6 +221,7 @@ def _tap_data_from_class_or_function(
218221
is_required=is_required,
219222
default=default,
220223
description=param_to_description.get(param.name),
224+
is_positional_only=param.kind == inspect.Parameter.POSITIONAL_ONLY,
221225
)
222226
args_data.append(arg_data)
223227
return _TapData(args_data, has_kwargs, known_only)
@@ -255,6 +259,10 @@ def _tap_data(class_or_function: _ClassOrFunction, param_to_description: Dict[st
255259

256260

257261
def _tap_class(args_data: Sequence[_ArgData]) -> Type[Tap]:
262+
"""
263+
Transfers argument data to a :class:`tap.Tap` class. Arguments will be added to the parser on initialization.
264+
"""
265+
258266
class ArgParser(Tap):
259267
# Overwriting configure would force a user to remember to call super().configure if they want to overwrite it
260268
# Instead, overwrite _configure
@@ -298,14 +306,14 @@ def tapify(
298306
299307
:param class_or_function: The class or function to run with the provided arguments.
300308
:param known_only: If true, ignores extra arguments and only parses known arguments.
301-
:param command_line_args: A list of command line style arguments to parse (e.g., ['--arg', 'value']).
302-
If None, arguments are parsed from the command line (default behavior).
303-
:param explicit_bool: Booleans can be specified on the command line as "--arg True" or "--arg False"
304-
rather than "--arg". Additionally, booleans can be specified by prefixes of True and False
305-
with any capitalization as well as 1 or 0.
306-
:param func_kwargs: Additional keyword arguments for the function. These act as default values when
307-
parsing the command line arguments and overwrite the function defaults but
308-
are overwritten by the parsed command line arguments.
309+
:param command_line_args: A list of command line style arguments to parse (e.g., ['--arg', 'value']). If None,
310+
arguments are parsed from the command line (default behavior).
311+
:param explicit_bool: Booleans can be specified on the command line as "--arg True" or "--arg False" rather than
312+
"--arg". Additionally, booleans can be specified by prefixes of True and False with any
313+
capitalization as well as 1 or 0.
314+
:param func_kwargs: Additional keyword arguments for the function. These act as default values when parsing the
315+
command line arguments and overwrite the function defaults but are overwritten by the parsed
316+
command line arguments.
309317
"""
310318
# We don't directly call to_tap_class b/c we need tap_data, not just tap_class
311319
docstring = _docstring(class_or_function)
@@ -324,13 +332,21 @@ def tapify(
324332
# Parse command line arguments
325333
command_line_args: Tap = tap.parse_args(args=command_line_args, known_only=known_only)
326334

327-
# Get command line arguments as a dictionary
335+
# Prepare command line arguments for class_or_function, respecting positional-only args
336+
class_or_function_args: list[Any] = []
337+
class_or_function_kwargs: Dict[str, Any] = {}
328338
command_line_args_dict = command_line_args.as_dict()
339+
for arg_data in tap_data.args_data:
340+
arg_value = command_line_args_dict[arg_data.name]
341+
if arg_data.is_positional_only:
342+
class_or_function_args.append(arg_value)
343+
else:
344+
class_or_function_kwargs[arg_data.name] = arg_value
329345

330346
# Get **kwargs from extra command line arguments
331347
if tap_data.has_kwargs:
332348
kwargs = {tap.extra_args[i].lstrip("-"): tap.extra_args[i + 1] for i in range(0, len(tap.extra_args), 2)}
333-
command_line_args_dict.update(kwargs)
349+
class_or_function_kwargs.update(kwargs)
334350

335351
# Initialize the class or run the function with the parsed arguments
336-
return class_or_function(**command_line_args_dict)
352+
return class_or_function(*class_or_function_args, **class_or_function_kwargs)

0 commit comments

Comments
 (0)