4
4
import traceback
5
5
import typing as ty
6
6
import warnings
7
+ from __future__ import annotations
7
8
8
9
try :
9
- import asyncclick as click
10
+ import asyncclick
11
+
12
+ ASYNCCLICK_SUPPORT = True
10
13
except ImportError :
14
+ ASYNCCLICK_SUPPORT = False
15
+ try :
11
16
import click
12
- import click .core
17
+
18
+ CLICK_SUPPORT = True
19
+ except ImportError as err :
20
+ CLICK_SUPPORT = False
21
+ if ASYNCCLICK_SUPPORT :
22
+ pass
23
+ else :
24
+ raise err
13
25
from docutils import nodes
14
26
from docutils .parsers import rst
15
27
from docutils .parsers .rst import directives
28
40
29
41
ANSI_ESC_SEQ_RE = re .compile (r'\x1B\[\d+(;\d+){0,2}m' , flags = re .MULTILINE )
30
42
31
- _T_Formatter = ty .Callable [[click .Context ], ty .Generator [str , None , None ]]
43
+ if ASYNCCLICK_SUPPORT and CLICK_SUPPORT :
44
+ click_context_type = asyncclick .Context | click .Context
45
+ click_option_type = asyncclick .core .Option | click .core .Option
46
+ click_choice_type = asyncclick .Choice | click .Choice
47
+ click_argument_type = asyncclick .Argument | click .Argument
48
+ click_command_type = asyncclick .Command | click .Command
49
+ click_multicommand_type = asyncclick .MultiCommand | click .MultiCommand
50
+ click_group_type = asyncclick .Group | click .Group
51
+ click_command_collection_type = (
52
+ asyncclick .CommandCollection | click .CommandCollection
53
+ )
54
+ join_options = click .formatting .join_options
55
+ elif ASYNCCLICK_SUPPORT :
56
+ click_context_type = asyncclick .Context
57
+ click_option_type = asyncclick .core .Option
58
+ click_choice_type = asyncclick .Choice
59
+ click_argument_type = asyncclick .Argument
60
+ click_command_type = asyncclick .Command
61
+ click_multicommand_type = asyncclick .MultiCommand
62
+ click_group_type = asyncclick .Group
63
+ click_command_collection_type = asyncclick .CommandCollection
64
+ join_options = asyncclick .formatting .join_options
65
+ else :
66
+ click_context_type = click .Context
67
+ click_option_type = click .core .Option
68
+ click_choice_type = click .Choice
69
+ click_argument_type = click .Argument
70
+ click_command_type = click .Command
71
+ click_multicommand_type = click .MultiCommand
72
+ click_group_type = click .Group
73
+ click_command_collection_type = click .CommandCollection
74
+ join_options = click .formatting .join_options
75
+
76
+ _T_Formatter = ty .Callable [[click_context_type ], ty .Generator [str , None , None ]]
32
77
33
78
34
79
def _process_lines (event_name : str ) -> ty .Callable [[_T_Formatter ], _T_Formatter ]:
35
80
def decorator (func : _T_Formatter ) -> _T_Formatter :
36
81
@functools .wraps (func )
37
- def process_lines (ctx : click .Context ) -> ty .Generator [str , None , None ]:
82
+ def process_lines (
83
+ ctx : click_context_type ,
84
+ ) -> ty .Generator [str , None , None ]:
38
85
lines = list (func (ctx ))
39
86
if "sphinx-click-env" in ctx .meta :
40
87
ctx .meta ["sphinx-click-env" ].app .events .emit (event_name , ctx , lines )
@@ -56,15 +103,18 @@ def prefixed_lines() -> ty.Generator[str, None, None]:
56
103
return '' .join (prefixed_lines ())
57
104
58
105
59
- def _get_usage (ctx : click . Context ) -> str :
106
+ def _get_usage (ctx : click_context_type ) -> str :
60
107
"""Alternative, non-prefixed version of 'get_usage'."""
61
108
formatter = ctx .make_formatter ()
62
109
pieces = ctx .command .collect_usage_pieces (ctx )
63
110
formatter .write_usage (ctx .command_path , ' ' .join (pieces ), prefix = '' )
64
111
return formatter .getvalue ().rstrip ('\n ' ) # type: ignore
65
112
66
113
67
- def _get_help_record (ctx : click .Context , opt : click .core .Option ) -> ty .Tuple [str , str ]:
114
+ def _get_help_record (
115
+ ctx : click_context_type ,
116
+ opt : click_option_type ,
117
+ ) -> ty .Tuple [str , str ]:
68
118
"""Re-implementation of click.Opt.get_help_record.
69
119
70
120
The variant of 'get_help_record' found in Click makes uses of slashes to
@@ -76,7 +126,7 @@ def _get_help_record(ctx: click.Context, opt: click.core.Option) -> ty.Tuple[str
76
126
"""
77
127
78
128
def _write_opts (opts : ty .List [str ]) -> str :
79
- rv , _ = click . formatting . join_options (opts )
129
+ rv , _ = join_options (opts )
80
130
if not opt .is_flag and not opt .count :
81
131
name = opt .name
82
132
if opt .metavar :
@@ -120,7 +170,7 @@ def _write_opts(opts: ty.List[str]) -> str:
120
170
)
121
171
)
122
172
123
- if isinstance (opt .type , click . Choice ):
173
+ if isinstance (opt .type , click_choice_type ):
124
174
extras .append (':options: %s' % ' | ' .join (str (x ) for x in opt .type .choices ))
125
175
126
176
if extras :
@@ -150,7 +200,9 @@ def _format_help(help_string: str) -> ty.Generator[str, None, None]:
150
200
151
201
152
202
@_process_lines ("sphinx-click-process-description" )
153
- def _format_description (ctx : click .Context ) -> ty .Generator [str , None , None ]:
203
+ def _format_description (
204
+ ctx : click_context_type ,
205
+ ) -> ty .Generator [str , None , None ]:
154
206
"""Format the description for a given `click.Command`.
155
207
156
208
We parse this as reStructuredText, allowing users to embed rich
@@ -162,7 +214,9 @@ def _format_description(ctx: click.Context) -> ty.Generator[str, None, None]:
162
214
163
215
164
216
@_process_lines ("sphinx-click-process-usage" )
165
- def _format_usage (ctx : click .Context ) -> ty .Generator [str , None , None ]:
217
+ def _format_usage (
218
+ ctx : click_context_type ,
219
+ ) -> ty .Generator [str , None , None ]:
166
220
"""Format the usage for a `click.Command`."""
167
221
yield '.. code-block:: shell'
168
222
yield ''
@@ -172,7 +226,8 @@ def _format_usage(ctx: click.Context) -> ty.Generator[str, None, None]:
172
226
173
227
174
228
def _format_option (
175
- ctx : click .Context , opt : click .core .Option
229
+ ctx : click_context_type ,
230
+ opt : click_option_type ,
176
231
) -> ty .Generator [str , None , None ]:
177
232
"""Format the output for a `click.core.Option`."""
178
233
opt_help = _get_help_record (ctx , opt )
@@ -194,13 +249,15 @@ def _format_option(
194
249
195
250
196
251
@_process_lines ("sphinx-click-process-options" )
197
- def _format_options (ctx : click .Context ) -> ty .Generator [str , None , None ]:
252
+ def _format_options (
253
+ ctx : click_context_type ,
254
+ ) -> ty .Generator [str , None , None ]:
198
255
"""Format all `click.Option` for a `click.Command`."""
199
256
# the hidden attribute is part of click 7.x only hence use of getattr
200
257
params = [
201
258
param
202
259
for param in ctx .command .params
203
- if isinstance (param , click . core . Option ) and not getattr (param , 'hidden' , False )
260
+ if isinstance (param , click_option_type ) and not getattr (param , 'hidden' , False )
204
261
]
205
262
206
263
for param in params :
@@ -209,7 +266,9 @@ def _format_options(ctx: click.Context) -> ty.Generator[str, None, None]:
209
266
yield ''
210
267
211
268
212
- def _format_argument (arg : click .Argument ) -> ty .Generator [str , None , None ]:
269
+ def _format_argument (
270
+ arg : click_argument_type ,
271
+ ) -> ty .Generator [str , None , None ]:
213
272
"""Format the output of a `click.Argument`."""
214
273
yield '.. option:: {}' .format (arg .human_readable_name )
215
274
yield ''
@@ -228,9 +287,11 @@ def _format_argument(arg: click.Argument) -> ty.Generator[str, None, None]:
228
287
229
288
230
289
@_process_lines ("sphinx-click-process-arguments" )
231
- def _format_arguments (ctx : click .Context ) -> ty .Generator [str , None , None ]:
290
+ def _format_arguments (
291
+ ctx : click_context_type ,
292
+ ) -> ty .Generator [str , None , None ]:
232
293
"""Format all `click.Argument` for a `click.Command`."""
233
- params = [x for x in ctx .command .params if isinstance (x , click . Argument )]
294
+ params = [x for x in ctx .command .params if isinstance (x , click_argument_type )]
234
295
235
296
for param in params :
236
297
for line in _format_argument (param ):
@@ -239,13 +300,13 @@ def _format_arguments(ctx: click.Context) -> ty.Generator[str, None, None]:
239
300
240
301
241
302
def _format_envvar (
242
- param : ty . Union [ click . core . Option , click . Argument ] ,
303
+ param : click_option_type | click_argument_type ,
243
304
) -> ty .Generator [str , None , None ]:
244
305
"""Format the envvars of a `click.Option` or `click.Argument`."""
245
306
yield '.. envvar:: {}' .format (param .envvar )
246
307
yield ' :noindex:'
247
308
yield ''
248
- if isinstance (param , click . Argument ):
309
+ if isinstance (param , click_argument_type ):
249
310
param_ref = param .human_readable_name
250
311
else :
251
312
# if a user has defined an opt with multiple "aliases", always use the
@@ -256,7 +317,9 @@ def _format_envvar(
256
317
257
318
258
319
@_process_lines ("sphinx-click-process-envars" )
259
- def _format_envvars (ctx : click .Context ) -> ty .Generator [str , None , None ]:
320
+ def _format_envvars (
321
+ ctx : click_context_type ,
322
+ ) -> ty .Generator [str , None , None ]:
260
323
"""Format all envvars for a `click.Command`."""
261
324
262
325
auto_envvar_prefix = ctx .auto_envvar_prefix
@@ -281,7 +344,9 @@ def _format_envvars(ctx: click.Context) -> ty.Generator[str, None, None]:
281
344
yield ''
282
345
283
346
284
- def _format_subcommand (command : click .Command ) -> ty .Generator [str , None , None ]:
347
+ def _format_subcommand (
348
+ command : click_command_type ,
349
+ ) -> ty .Generator [str , None , None ]:
285
350
"""Format a sub-command of a `click.Command` or `click.Group`."""
286
351
yield '.. object:: {}' .format (command .name )
287
352
@@ -296,7 +361,9 @@ def _format_subcommand(command: click.Command) -> ty.Generator[str, None, None]:
296
361
297
362
298
363
@_process_lines ("sphinx-click-process-epilog" )
299
- def _format_epilog (ctx : click .Context ) -> ty .Generator [str , None , None ]:
364
+ def _format_epilog (
365
+ ctx : click_context_type ,
366
+ ) -> ty .Generator [str , None , None ]:
300
367
"""Format the epilog for a given `click.Command`.
301
368
302
369
We parse this as reStructuredText, allowing users to embed rich
@@ -306,7 +373,9 @@ def _format_epilog(ctx: click.Context) -> ty.Generator[str, None, None]:
306
373
yield from _format_help (ctx .command .epilog )
307
374
308
375
309
- def _get_lazyload_commands (ctx : click .Context ) -> ty .Dict [str , click .Command ]:
376
+ def _get_lazyload_commands (
377
+ ctx : click_context_type ,
378
+ ) -> ty .Dict [str , click_command_type ]:
310
379
commands = {}
311
380
for command in ctx .command .list_commands (ctx ):
312
381
commands [command ] = ctx .command .get_command (ctx , command )
@@ -315,12 +384,12 @@ def _get_lazyload_commands(ctx: click.Context) -> ty.Dict[str, click.Command]:
315
384
316
385
317
386
def _filter_commands (
318
- ctx : click . Context ,
387
+ ctx : click_context_type ,
319
388
commands : ty .Optional [ty .List [str ]] = None ,
320
- ) -> ty .List [click . Command ]:
389
+ ) -> ty .List [click_command_type ]:
321
390
"""Return list of used commands."""
322
391
lookup = getattr (ctx .command , 'commands' , {})
323
- if not lookup and isinstance (ctx .command , click . MultiCommand ):
392
+ if not lookup and isinstance (ctx .command , click_multicommand_type ):
324
393
lookup = _get_lazyload_commands (ctx )
325
394
326
395
if commands is None :
@@ -330,7 +399,7 @@ def _filter_commands(
330
399
331
400
332
401
def _format_command (
333
- ctx : click . Context ,
402
+ ctx : click_context_type ,
334
403
nested : NestedT ,
335
404
commands : ty .Optional [ty .List [str ]] = None ,
336
405
) -> ty .Generator [str , None , None ]:
@@ -429,7 +498,7 @@ class ClickDirective(rst.Directive):
429
498
'show-nested' : directives .flag ,
430
499
}
431
500
432
- def _load_module (self , module_path : str ) -> ty . Union [ click . Command , click . Group ] :
501
+ def _load_module (self , module_path : str ) -> click_command_type | click_group_type :
433
502
"""Load the module."""
434
503
435
504
try :
@@ -460,7 +529,7 @@ def _load_module(self, module_path: str) -> ty.Union[click.Command, click.Group]
460
529
461
530
parser = getattr (mod , attr_name )
462
531
463
- if not isinstance (parser , ( click . Command , click . Group ) ):
532
+ if not isinstance (parser , click_command_type | click_group_type ):
464
533
raise self .error (
465
534
'"{}" of type "{}" is not click.Command or click.Group.'
466
535
'"click.BaseCommand"' .format (type (parser ), module_path )
@@ -470,8 +539,8 @@ def _load_module(self, module_path: str) -> ty.Union[click.Command, click.Group]
470
539
def _generate_nodes (
471
540
self ,
472
541
name : str ,
473
- command : click . Command ,
474
- parent : ty .Optional [click . Context ],
542
+ command : click_command_type ,
543
+ parent : ty .Optional [click_context_type ],
475
544
nested : NestedT ,
476
545
commands : ty .Optional [ty .List [str ]] = None ,
477
546
semantic_group : bool = False ,
@@ -490,7 +559,10 @@ def _generate_nodes(
490
559
`click.CommandCollection`.
491
560
:returns: A list of nested docutil nodes
492
561
"""
493
- ctx = click .Context (command , info_name = name , parent = parent )
562
+ if ASYNCCLICK_SUPPORT and isinstance (command , asyncclick .Command ):
563
+ ctx = asyncclick .Context (command , info_name = name , parent = parent )
564
+ else :
565
+ ctx = click .Context (command , info_name = name , parent = parent )
494
566
495
567
if command .hidden :
496
568
return []
@@ -523,7 +595,7 @@ def _generate_nodes(
523
595
# Subcommands
524
596
525
597
if nested == NESTED_FULL :
526
- if isinstance (command , click . CommandCollection ):
598
+ if isinstance (command , click_command_collection_type ):
527
599
for source in command .sources :
528
600
section .extend (
529
601
self ._generate_nodes (
0 commit comments