60
60
61
61
# stdlib
62
62
import inspect
63
- from typing import Any , Callable , List , Optional , cast
63
+ from typing import Any , Callable , List , Optional , TypeVar , cast
64
64
65
65
# 3rd party
66
66
import click
67
- from click import Context , Option , OptionParser
67
+ from click import Argument , Context , Option , OptionParser
68
68
from click .decorators import _param_memo # type: ignore
69
69
70
70
# this package
71
+ import consolekit .input
71
72
from consolekit ._types import Callback , _ConvertibleType
72
73
73
74
__all__ = [
81
82
"auto_default_option" ,
82
83
]
83
84
85
+ _A = TypeVar ("_A" , bound = click .Argument )
86
+ _C = TypeVar ("_C" , bound = click .Command )
84
87
85
- def verbose_option (help_text : str = "Show verbose output." ) -> Callable :
88
+
89
+ def verbose_option (help_text : str = "Show verbose output." ) -> Callable [..., click .Command ]:
86
90
"""
87
91
Adds an option (via the parameter ``verbose``: :class:`int`) to enable verbose output.
88
92
@@ -101,15 +105,32 @@ def verbose_option(help_text: str = "Show verbose output.") -> Callable:
101
105
)
102
106
103
107
104
- def version_option (callback : Callable [[Context , Option , int ], Any ]) -> Callable :
108
+ def version_option (callback : Callable [[Context , Option , int ], Any ]) -> Callable [..., click . Command ] :
105
109
"""
106
110
Adds an option to show the version and exit.
107
111
108
112
The option can be provided multiple times by the user.
113
+ The count is stored as an integer and passed as the third parameter to the callback function.
109
114
110
115
.. versionadded:: 0.4.0
111
116
112
117
:param callback: The callback to invoke when the option is provided.
118
+
119
+ The callback function might look like:
120
+
121
+ .. code-block:: python
122
+
123
+ def version_callback(ctx: click.Context, param: click.Option, value: int):
124
+ if not value or ctx.resilient_parsing:
125
+ return
126
+
127
+ if value > 1:
128
+ click.echo(f"consolekit version {__version__}, Python {sys.version}")
129
+ else:
130
+ click.echo(f"consolekit version {__version__}")
131
+
132
+ ctx.exit()
133
+
113
134
"""
114
135
115
136
return click .option (
@@ -122,7 +143,7 @@ def version_option(callback: Callable[[Context, Option, int], Any]) -> Callable:
122
143
)
123
144
124
145
125
- def colour_option (help_text = "Whether to use coloured output." ) -> Callable :
146
+ def colour_option (help_text = "Whether to use coloured output." ) -> Callable [..., click . Command ] :
126
147
"""
127
148
Adds an option (via the parameter ``colour``: :class:`bool`) to enable verbose output.
128
149
@@ -138,7 +159,7 @@ def colour_option(help_text="Whether to use coloured output.") -> Callable:
138
159
)
139
160
140
161
141
- def force_option (help_text : str ) -> Callable :
162
+ def force_option (help_text : str ) -> Callable [..., click . Command ] :
142
163
"""
143
164
Decorator to add the ``-f / --force`` option to a click command.
144
165
@@ -150,10 +171,12 @@ def force_option(help_text: str) -> Callable:
150
171
return flag_option ("-f" , "--force" , help = help_text )
151
172
152
173
153
- def no_pager_option (help_text = "Disable the output pager." ) -> Callable :
174
+ def no_pager_option (help_text = "Disable the output pager." ) -> Callable [..., click . Command ] :
154
175
"""
155
176
Decorator to add the ``--no-pager`` option to a click command.
156
177
178
+ The value is exposed via the parameter ``no_pager``: :class:`bool`.
179
+
157
180
.. versionadded:: 0.5.0
158
181
159
182
:param help_text: The help text for the option.
@@ -162,7 +185,7 @@ def no_pager_option(help_text="Disable the output pager.") -> Callable:
162
185
return flag_option ("--no-pager" , help = help_text )
163
186
164
187
165
- def flag_option (* args , default : Optional [bool ] = False , ** kwargs ) -> Callable :
188
+ def flag_option (* args , default : Optional [bool ] = False , ** kwargs ) -> Callable [..., click . Command ] :
166
189
r"""
167
190
Decorator to a flag option to a click command.
168
191
@@ -181,7 +204,7 @@ def flag_option(*args, default: Optional[bool] = False, **kwargs) -> Callable:
181
204
)
182
205
183
206
184
- def auto_default_option (* param_decls , ** attrs ) -> Callable :
207
+ def auto_default_option (* param_decls , ** attrs ) -> Callable [..., click . Command ] :
185
208
"""
186
209
Attaches an option to the command, with a default value determined from the decorated function's signature.
187
210
@@ -195,7 +218,7 @@ def auto_default_option(*param_decls, **attrs) -> Callable:
195
218
:param cls: the option class to instantiate. This defaults to :class:`click.Option`.
196
219
"""
197
220
198
- def decorator (f ) :
221
+ def decorator (f : _C ) -> _C :
199
222
option_attrs = attrs .copy ()
200
223
201
224
if "help" in option_attrs :
@@ -234,15 +257,30 @@ class MultiValueOption(click.Option):
234
257
The later is converted into the former automatically if supported.
235
258
:param required: Controls whether this is optional.
236
259
:param default: The default value if omitted.
237
- This can also be a callable, in which case it's invoked when the default is needed without any arguments.
260
+ This can also be a callable, in which case it is invoked when the default is needed without any arguments.
238
261
:param callback: A callback that should be executed after the parameter was matched.
239
262
This is called as ``fn(ctx, param, value)`` and needs to return the value.
240
263
:param metavar: How the value is represented in the help page.
241
264
:param expose_value: If :py:obj:`True` then the value is passed onwards to the command callback
242
- and stored on the context, otherwise it's skipped.
265
+ and stored on the context, otherwise it is skipped.
243
266
:param is_eager: Eager values are processed before non eager ones.
244
267
245
268
.. versionadded:: 0.6.0
269
+
270
+ Example usage:
271
+
272
+ .. code-block:: python
273
+
274
+ @click.option(
275
+ "--select",
276
+ type=click.STRING,
277
+ help="The checks to enable",
278
+ cls=MultiValueOption,
279
+ )
280
+ @click_command()
281
+ def main(select: Iterable[str]):
282
+ select = list(select)
283
+
246
284
"""
247
285
248
286
def __init__ (
@@ -312,3 +350,30 @@ def parser_process(value, state):
312
350
break
313
351
314
352
return retval
353
+
354
+
355
+ class _Option (click .Option ):
356
+
357
+ def prompt_for_value (self , ctx ):
358
+ """
359
+ This is an alternative flow that can be activated in the full value processing if a value does not exist.
360
+
361
+ It will prompt the user until a valid value exists and then returns the processed value as result.
362
+ """
363
+
364
+ # Calculate the default before prompting anything to be stable.
365
+ default = self .get_default (ctx )
366
+
367
+ # If this is a prompt for a flag we need to handle this differently.
368
+ if self .is_bool_flag :
369
+ return consolekit .input .confirm (self .prompt , default )
370
+
371
+ return consolekit .input .prompt (
372
+ self .prompt ,
373
+ default = default ,
374
+ type = self .type ,
375
+ hide_input = self .hide_input ,
376
+ show_choices = self .show_choices ,
377
+ confirmation_prompt = self .confirmation_prompt ,
378
+ value_proc = lambda x : self .process_value (ctx , x ),
379
+ )
0 commit comments