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